Bootstrap

Java中文件读写——File类,IO流,字符流,字节流

File类是什么?
1.在读写数据时告诉JVM要操作的文件或文件夹在哪。
2.对文件或文件夹本身进行操作。包括创建,修改,删除等操作。

IO流是什么?
1.可以将数据从本地文件中读取出来。
2.可以将数据从内存中保存到本地文件。

File类的构造方法
1.File时文件和目录路径名的抽象表示。
2.文件和目录可以被File封装成对象。
3.File封装的对象仅仅是一个路径名,它可以存在也可以不存在。
4.构造方法:

  1. File(String pathname) // 通过给定路径名字符串转换为抽象路径名来创建File实例。
  2. File(String parent, String child) // 从父路径名字符串和子路径名字符串创建File实例。
  3. File(File parent, String child) // 从父抽象路径名和子路径名字符串创建File实例。

解释:为了使用File中的方法。所以要把路径封装成字符串给File。

//三种构造方法创建a.txt文件
        File f1 = new File("D:\\DeskTop\\a.txt");

        String path1 = "D:\\DeskTop";
        String path2 = "a.txt";
        File f2 = new File(path1, path2);

        File ff = new File("D:\\DeskTop");
        File f3 = new File(ff, path2);

绝对路径和相对路径
1.绝对路径:从盘符开始的路径的完整串。
2.相对路径:相对当前项目而言的路径。

File的创建功能
1.public boolean createNewFile() // 创建一个新的空的文件。
2.public boolean mkdir() // 创建一个文件夹,不管调用者有没有后缀,它只能创建文件夹。
3.public boolean mkdirs() // 创建一个多级文件夹,也可以创建单级文件夹,不管调用者有没有后缀,它只能创建文件夹。

File类的删除方法
1.public boolean delete() // 删除文件,或文件夹。
注意:

  1. 该方法删除是直接从硬盘删除,不进回收站。
  2. 删除文件夹时只能删除空的文件夹,不能删除有内容的文件夹。
  3. 若想删除有内容的文件夹,只能先进入这个文件夹将里面内容删空后再删该文件夹。

File类的获取和判断功能
1.public boolean isDirectory() // 判断此抽象路径名表示的File是否为目录。
2.public boolean isFile() // 判断此抽象路径名表示的File是否为文件。
3.public boolean exists() // 判断此抽象路径名表示的File是否存在。
4.public String getName() // 获取由此抽象路径名表示的文件或目录的名称。

File类的高级获取功能
1.public File[] listFiles() // 返回目录中所有的文件和文件夹的File数组。
注意:

  1. 当调用者时一个文件时:返回null。
  2. 当调用者是一个空文件夹时:返回File类型的空数组。
  3. 当调用者是一个有权限才能进入的文件夹时:返回null。

IO流
目的:
1.写数据,将数据写到文件中,实现永久化存储。
2.读数据,将文件中的数据读取至内存。
3.I 也就是input,O也就是output。
4.以内存为参照物进行读写。

IO流的分类
在这里插入图片描述
什么是纯文本文件:
1.Windows记事本能打开的文件,并且内容没有出现乱码的文件。

字节流

输入输出是以内存为参照,从内存写到硬盘,数据从内存流出了所以是输出。输入流则是从硬盘读取到内存的流。
1.写数据:

// 创建字节输出流对象
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\a.txt");
        // 写数据
        fileOutputStream.write(97); // 写入字符a
        // 关闭流,释放资源
        fileOutputStream.close();

结论:

  1. 若写的文件存在,会把文件清空并写出指定内容。
  2. 若文件不存在,会自动创建一个。

写数据的三种方式
在这里插入图片描述
注意:
1.写换行符时:windows : \r\n,Linux:\n,Mac:\r。
如何写数据时不清空原来的内容:

// 创建字节输出流对象,续写开关 true打开,false关闭
        FileOutputStream fileOutputStream = 
        new FileOutputStream("D:\\a.txt", true);
        

try…catch捕获异常

		FileOutputStream fileOutputStream = null;
        try {
            // 创建字节输出流对象,续写开关 true打开,false关闭
            fileOutputStream = new FileOutputStream("D:\\a.txt", true);
            // 写数据
            fileOutputStream.write(97);

        }catch (Exception e){
            e.printStackTrace();
        } finally { // 此处代码不管有没有触发异常一定会被执行
            // 关闭流
            if(fileOutputStream != null){ // 若创建对象之前有代码可能没有到创建字节输出流时发生异常。
                try{
                    fileOutputStream.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }

2.读数据:
字节输入流

		FileInputStream file = null;
        try {
            // 创建字节输入流
            file = new FileInputStream("D:\\a.txt");
            int read = file.read();
            System.out.println(read);
        }catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(file != null){
                try{
                    file.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }

结论:

  1. read()方法读取的是字节信息,若像看字符需要强转为char类型。
  2. 一次只能读取一个字节,返回值是读到的当前字节码点数据。
  3. 读取的文件必须存在。

读取多个数据:

// 创建字节输入流
            file = new FileInputStream("D:\\a.txt");
            int read = 0;
            while((read = file.read()) != -1) {
                System.out.println((char)read);
            }
            file.close();

文件复制

// 文件复制
        FileInputStream fread = null;
        FileOutputStream fwrite = null;
        try{
            fread = new FileInputStream("D:\\a.txt");
            fwrite = new FileOutputStream("D:\\b.txt");

            int r = 0;
            while((r = fread.read()) != -1){
                fwrite.write(r);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(fread != null){
                try {
                    fread.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if(fwrite != null){
                try {
                    fwrite.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }

定义小数组拷贝
上述文件复制的效率相当慢,第一个字节写一个字节,大大降低了效率。若能一次读完再写出,那将会大大提高效率,优化代码如下。

		FileInputStream fread = null;
        FileOutputStream fwrite = null;
        try{
            fread = new FileInputStream("D:\\桌面\\a.txt");
            fwrite = new FileOutputStream("D:\\桌面\\b.txt");

            byte[] r = new byte[1024]; // 一次读取多个字节放入其中
            int len;
            while((len = fread.read()) != -1){ // 此时read()方法返回的是读取到的有效字节的个数
                fwrite.write(r, 0, len);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(fread != null){
                try {
                    fread.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if(fwrite != null){
                try {
                    fwrite.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }

原理:
在这里插入图片描述

字符流

既然字节流可以操作所有文件,为什么还要存在字符流。

字节流在读写中文时有可能出现乱码。
原因:
1.计算机中信息的存储都是二进制表示的。
2.编码:按照某种规则,将字符变成二进制再存入计算机中,这个过程称为编码。
3.解码:以相同规则,将字符的二进制表示方式变成字符的过程称为解码。
4.编码和解码的方式应该保持一致,若不一致则会导致乱码。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
所以可以总结一下乱码原因:
1.字节流一次只能读取一个字节,而中文再IDE中默认UTF-8编码的,一个汉字对应三个字节,所以每次读到的是不完整的,所以出现了乱码。

字符流
底层:字节流+编码表 来实现。
注意:只要是中文,不管在哪个码表中,第一个字节一定是负数。
1.字符输出流:
构造: FileWriter类
字符流写数据的 5 种方式:

  1. void write(int c) // 写一个字符
  2. void write(char[] cbuf) // 写一个字符数组
  3. void write(char[] cbuf,int off,int len) // 写一个字符数组的一部分
  4. void write(String s) // 写一个字符串
  5. void write(String s,int off,int len) // 写一个字符串的一部分

注意:

  1. 若创建对象文件不存在,自动创建文件并写出。
  2. 文件夫路径必须存在。
  3. 创建字符输出流对象如果文件已经存在,则会自动清空文件并写出。

2.字符输入流:
构造: FileReader类

缓冲流

字节缓冲输入流
构造方法:
1.BufferedInputStream(InputStream in) // 创建一个缓冲流放入参数供以后使用,此时缓冲流默认8192长度的byte数组。
2.BufferedInputStream(InputStream in, int size) // 创建一个有指定大小的缓冲流放入参数供以后使用。

字节缓冲输出流
构造方法:
1.BufferedOutputStream(OutputStream in) // 创建一个缓冲流放入参数供以后使用,此时缓冲流默认8192个长度的byte数组。
2.BufferedOutputStream(OutputStream in, int size) // 创建一个有指定大小的缓冲流放入参数供以后使用。

为什么入参需要的是一个字节流,而不是文件路径?
因为缓冲流仅仅提供一个缓冲区,而真正的读写数据还得是基本字节流对象对数据的具体操作。

原理:
在这里插入图片描述

可优化部分
在这里插入图片描述
字符缓冲输入流
特有方法:
1.String readLine() // 自动读取一整行内容,但是不会读到换行符。该方法只有BufferedReader中有。若读不到数据返回null而不是-1。
2.BufferedReader:可将文件中的数据高效读取。

// 创建字符输入缓冲流对象
        BufferedReader file = new BufferedReader(new FileReader("D:\\a.txt"));

字符缓冲输出流
特有方法:
1.void newLine() // 自动写出换行符。该方法只有BufferedWriter中有。该方法可一自动识别操作系统换行符所系要的类型。具有跨平台性。
BufferedWriter:可将字符流中的数据高效写出。
2.BufferedWriter:可将字符流中的数据高效写出。

// 创建字符输出缓冲流对象
        BufferedWriter file = new BufferedWriter(new FileWriter("D:\\a.txt"));

flush和close方法
1.flush() // 该方法 刷新流,继续使用,刷新时将数据从流刷到目标中。
2.close() // 该方法 关闭流,但在关闭之前先刷新流,关闭后该流不能再使用。

字节流和字符流的使用场景
1.文件拷贝:一律使用字节流。
2.文件读取:使用字符流的字符输入流。
3.文件写出:使用字符流的字符输出流。

;