Bootstrap

【JavaEE】文件io

目录

文件类型

File概述

属性

构造方法

常用方法

Reader

Writer

InputStream

OutputStream

字节流转字符流 

通过Scanner读取InputStream

通过PrintWriter转换outputstream

示例 


文件类型

从编程的角度看,文件类型主要就是两大类

  • 文本(文件中保存的数据,都是字符串,保存的内容都是合法的字符)
  • 二进制(文件中保存的数据,仅仅是二进制数据,不要求保存的内容是合法的字符)

File概述

属性

修饰符及类型属性说明
static StringpathSeparator依赖于系统的路径分隔符,String类型表示
static charpathSeparator依赖于系统的路径分隔符,char类型表示

 D:\

如上所示,“\”就是一个路径分隔符 

构造方法

签名说明
File(File parent,String child)根据父目录+孩子文件路径,创建一个新的File实例
File(String pathname)根据文件路径创建你一个新的File实例,路径可以是绝对路径或者相对路径
File(String parent,String chile)根据父目录+孩子我呢见路径,创建一个新的File实例,父目录用路径表示

常用方法

修饰符及返回类型方法签名说明
StringgetParent()返回File对象的父目录文件路径
StringgetName()返回File对象的纯文本名称
String        getPath()返回File对象的文件路径
StringgetAbsolutePath()返回File对象的绝对路径
StringgetCanonicalPath()返回File对象修饰过的绝对路径
booleanexists()判断File对象描述的文件是否真实存在
booleanisDirectory()判断File对象代表的文件是否是一个目录
booleanisFile()判断File对象代表的文件是否是一个普通文件
booleancreateNewFile()根据File对象,自动创建一个空文件,成功创建返回true
booleandelete()根据File对象,删除该文件
voiddeleteOnExit()根据File对象,标注文件将被删除,删除动作会到JVM运行结束时才删除
String[]list()返回File对象代表的目录下所有文件名
File[]listFiles()返回File对象代表的目录下的所有文件,以File对象表示
booleanmkdir()创建File对象代表的目录
booleanmkdirs()创建File对象代表的目录,如果必要,会创建中间目录
booleanrenameTo(File dest)进行文件改名,也可以视为平时的剪切、粘贴操作
booleancanRead()判断用户是否对文件有可读权限
booleancanWrite()判断用户是否对文件有可写权限

在标准库中,提供的读写文件的流对象,有很多个类,可以归结为两个大的类别中:

1、字节流(对应着二进制文件),每次读/写的最小单位,都是“字节”

  • InputStream
  • OutputStream

2、字符流(对应着文本文件) ,每次读/写的最小单位,是“字符”,一个字符可能对应多个字节,跟当前字符集编码方式有关,本质上,是针对字节流进行了又一层封装

  • Reader
  • Writer

上述四种是抽象类,不能直接new实例

  1. 无参数read:一次读取一个字符
  2. 一个参数read:一次读取若干个字符,会把参数指定的buf数组给填充满
  3. 三个参数read:一次读取若干个字符,会把参数执行的cbuf数组中,从off这个位置开始,到len这么长的范围尽量填满

使用示例:

Reader

一次read一个字符

try(Reader reader = new FileReader("D:/data.txt");){
    while (true) {
        int c = reader.read();
        if (c == -1) {
            //读取完毕
            break;
        }
        char ch = (char) c;
        System.out.println(ch);
    }     
} catch (IOException e) {
    throw new RuntimeException(e);
}

read方法一次读取一个字符,返回数字,当返回值为-1时,代表读取到文件末尾

一次read多个字符

Reader reader=new FileReader("D:/data.txt");
try{
    while(true){
        char[] ch=new char[1024];
        int n= reader.read(ch);
        if(n==-1)
        {
            //读取完毕
            break;
        }
        System.out.println("读取长度:"+n);
        System.out.println(Arrays.toString(ch));
    }
} catch (IOException e) {
    throw new RuntimeException(e);
} finally {
    try {
        reader.close();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

这样写虽然能够解决中途抛异常,导致reader不能正常释放,但是不够优雅,我们可以采取try with resources方法 ,Reader reader=new FileReader("D:/data.txt")在出了作用域后,会自动调用close方法

try(Reader reader=new FileReader("D:/data.txt")){
    while(true){
        char[] ch=new char[1024];
        int n= reader.read(ch);
        if(n==-1)
        {
            //读取完毕
            break;
        }
        System.out.println("读取长度:"+n);
        System.out.println(Arrays.toString(ch));
    }
} catch (IOException e) {
    throw new RuntimeException(e);

Writer

try(Writer writer=new FileWriter("D:/data.txt"))
{
    writer.write("你好你好");
} catch (IOException e) {
    throw new RuntimeException(e);
}

Writer写入文件,默认情况下,会将原来的数据清空掉,如果不想清空,需要在构造函数中再传入一个参数 ,这样就能实现追加写

Writer writer=new FileWriter("D:/data.txt",true)

utf8编码,一个中文字符是3个字节,在Java标准库内部:

  • 如果是只使用char,此时使用的字符集,固定就是unicode;
  • 如果是使用String,此时就会自动的把每个字符的unicode转换utf8

InputStream

字节流与字符流不同的是,传入的数组是byte类型的,read返回值是int,其实是byte,0-255之间的值,-1表示到达文件末尾

try(InputStream inputStream=new FileInputStream("D:/data.txt"))
{
    byte[] buffer=new byte[1024];
    int n=inputStream.read(buffer);
    System.out.println("n = "+n);
    for(int i=0;i<n;i++)
    {
        System.out.printf("%x\n",buffer[i]);
    }
} catch (IOException e) {
    throw new RuntimeException(e);
}

OutputStream

OutputStream和Writer类似,打开一个文件,默认就会清空文件的原有内容

try(OutputStream outputStream=new FileOutputStream("d:/data.txt"))
{
    String str="你好";
    outputStream.write(str.getBytes());
} catch (IOException e) {
    throw new RuntimeException(e);
}

若要使用追加的方式写入,可以在构造方法的第二个参数中传入true

OutputStream outputStream=new FileOutputStream("d:/data.txt",true)

字节流转字符流 

当提供的是字节流对象,但实际数据内容是文本数据,我们可以将字节流转换成字符流

通过Scanner读取InputStream

try(InputStream inputStream=new FileInputStream("d:/data.txt"))
{
    Scanner scanner=new Scanner(inputStream);
    String str=scanner.next();
    System.out.println(str);
} catch (IOException e) {
    throw new RuntimeException(e);
}

通过PrintWriter转换outputstream

try(OutputStream outputStream=new FileOutputStream("d:/data.txt"))
{
    PrintWriter writer=new PrintWriter(outputStream);
    writer.println("hello");
} catch (IOException e) {
    throw new RuntimeException(e);
}

PrintWriter这样的类,在进行写入的时候,不一定是直接写硬盘,而是先把数据写入到一个内存构成的缓冲区中,当我们写入缓冲区之后,如果还没来得及把缓冲区的数据写入硬盘中,进程就结束了,此时数据就丢失了,为了能够确保数据确实被写入硬盘,就应该在合适的时机,使用flush方法手动刷新缓冲区,因此上述的代码需要修改一下:

try(OutputStream outputStream=new FileOutputStream("d:/data.txt"))
{
    PrintWriter writer=new PrintWriter(outputStream);
    writer.println("hello");
    writer.flush();
} catch (IOException e) {
    throw new RuntimeException(e);
}

示例 

扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包括目录),并且后续询问用户是否要删除该文件

    public static void main(String[] args) throws FileNotFoundException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入搜索路径:");

        String direcory = scanner.next();
        File sourceFile = new File(direcory);
        if (!sourceFile.isDirectory()) {
            System.out.println("搜索路径有误!");
            return;
        }

        System.out.println("请输入要删除文件的关键词:");
        String word=scanner.next();
        scanDir(sourceFile,word);
    }

    public static void scanDir(File rootPath, String word)
    {
        File[] files = rootPath.listFiles();

        if(files==null)
            return;

        for (File file : files) {
            System.out.println("当前扫描的文件: " + file.getAbsolutePath());
            if (file.isFile()) {
                checkDelete(file,word);
            } else {
                scanDir(file,word);
            }
        }
    }
    public static void checkDelete(File rootPath, String word)
    {
        if(!rootPath.getName().contains(word))
        {
            //不必删除,直接方法结束
            return ;
        }
        //需要删除
        Scanner scanner = new Scanner(System.in);
        System.out.println("是否需要删除(Y/N):");
        String choice=scanner.next();
        if(choice.equals("Y")||choice.equals("N"))
        {
            rootPath.delete();
            System.out.println("删除完毕!");
        }
    }

;