064.JAVA输入输出_文件流


博主的 Github 地址


1. 文件流

  • 顾名思义, 就是用来访问和操作文件的流对象.

  • 文件流根据四大基流可以分为四种:

    • FileInputStream - 文件字节输入流
    • FileOutputStream - 文件字节输出流
    • FileReader - 文件字符输入流
    • FileWriter - 文件字符输出流
  • System 类中存在一种方法 getProperties() 可以获取当前系统的属性.

    • 其中 user.dir 这个 key 所对应的 value 则是用户的当前项目的根路径;

    • 因此在创建 File 对象时若没有指定文件的绝对路径, 只写入文件名称或相对路径,
      得到的 File 对象的所对应的文件绝对路径就是 user.dir 加上文件名或相对路径.

    • 例如当 user.dir = "C:/abc" 的时候,

      • 创建文件对象只给参数 String s = "123.txt", 调用 getAbsoluteFile() 方法得到的路径则为 "C:/abc/123.txt",
      • 如果给的参数为 String s = "file/123.txt", 那么得到的路径则为 "C:/abc/file/123.txt".

1.1. FileOutputStream 文件字节输出流

  • 文件输出流是用于将数据写入 FileFileDescriptor 的输出流.
  • FileOutputStream 用于写入诸如图像数据之类的原始字节的流, 如二进制操作.
  • 如果要写入字符流, 请考虑使用 FileWriter 进行操作, 如写入文字操作.
1.1.1. 常用构造器
  • FileOutputStream(File file)

    • 作用:
      创建一个向指定 File 对象表示的文件中写入数据的文件输出流
  • FileOutputStream(File file, boolean append)

    • 作用:
      创建一个向指定 File 对象表示的文件中写入数据的文件输出流
    • 备注:
      • 若第二个参数为 true, 则将字节写入文件内容末尾处, 即接上原有的字节续写.
      • 若第二个参数为 false, 则将字节写入文件内容开始处, 将之前的字节全部覆盖.
      • 在同一次开启文件输出流进行写入数据的时候, 会一直接着文件内容进行写入,
        只要输入流还没关闭, 不论调用多少次写入, 都是进行续写, 不会覆盖内容;
        只有不同次开启同一文件时, 才需要进行判定是否需要续写还是覆盖.
1.1.2. 常用方法
  • void write(int b)

    • 作用:
      将指定字节写入此文件输出流
  • void write(byte[] b)

    • 作用:
      b.length 个字节从指定 byte 数组写入此文件输出流中.
  • void write(byte[] b, int off, int len)

    • 作用:
      将指定 byte 数组中从 off 索引开始的 len 个字节写入此文件输出流
      注意, 是包括了 off 索引所指向的那一位.
1.1.3. 操作实例
public static void main(String[] args){
  //1. 创建目标对象, 表示存放到哪个文件. 如果文件不存在, 就会创建新文件.  
  //   但是目录若不存在则并不会进行创建, 因此写入的子文件夹或子目录必须要存在, 否则报错.
  File file = new Flie("file/testOutputStream.txt");

  //2. 创建文件字节输出流对象, 用来为字节流指向目标文件.
  OutputStream os = new FileOutputStream(file);

  //3. 具体的输出操作
  os.write(65); //将 `A` 写入目标文件当中
  os.write("ABCD".getBytes()); //将 "ABCD" 写入目标文件当中
  os.write("ABCD".getBytes(), 0, "ABCD".length()); //将 "ABCD" 写入目标文件当中

  //4. 关闭资源对象, 以防资源占用.
  os.close();
}

1.2. FileInputStream 文件字节输入流

  • 文件字节输入流用于从文件系统中的某个文件中获得输入字节.
  • 文件字节输入流用于读取诸如图像数据之类的原始字节流.
  • 若要读取字符流, 请考虑使用 FileReader.
1.2.1. 常用构造器
  • FileInputStream(File file)
    • 作用:
      通过打开一个到实际文件的连接来创建一个 FileInputStream, 该文件通过文件系统中的 File 对象 file 指定.
1.2.2. 常用方法
  • int read()

    • 作用:
      从此输入流中读取一个数据字节
    • 返回:
      连续调用将会把文件中的字节逐个读取返回, 直到没有字节后返回 -1.
  • int read(byte[] b)

    • 作用:
      从输入流中将最多 b.length 个字节的数据读取并存入到一个 byte 数组中.
      这个 byte 数组相当于一个缓冲区用于临时存放字节数据.
    • 返回:
      读入缓冲区的字节总数, 如果因为已经到达文件末尾而没有更多的数据, 则返回 -1.
  • int read(byte[] b, int off, int len)

    • 作用:
      从输入流中将最多 len 个字节的数据进行读取,
      并将字节存入一个 byte 数组中, 存放位置从 off 索引所对应的数组位置开始.
    • 备注:
      如果 len 不为 0, 则在输入可用之前, 该方法将阻塞;否则, 不读取任何字节并返回 0.
1.2.3. 操作实例
public static void main(String[] args){
  //1. 创建目标对象, 从哪个文件进行读取数据. 假设文件中存放 "ABCDEFG" 字符串. 
  File file = new Flie("file/testInputStream.txt");

  //2. 创建文件字节输入流对象, 用来为字节流指向目标文件.
  InputStream is = new FileInputStream(file);
  //同时定义一个byte数组, 用作缓冲区, 容量为10
  byte[] byte = new byte[10];

  //3. 具体的输出操作
  is.read(); //将 `A` 写入byte数组当中
  is.read(byte); //将 "ABCDEFG" 写入byte数组当中, 结果为 {65, 66, 67, 68, 69, 70, 71, 0, 0, 0}
  is.read(byte, 2, 6); //将 "ABCDEF" 写入byte数组当中, 从数组索引位置2开始存放, 结果为 {0, 0, 65, 66, 67, 68, 69, 70, 0}

  //4. 关闭资源对象, 以防资源占用.
  os.close();
}

1.3. 文件拷贝操作实例

public class Test {
    public static void main(String[] args) throws Exception {
        //1. 创建读取源和输出点
        File srcFile = new File("/Users/leon9dragon/Desktop/test.txt");
        File desFile = new File("/Users/leon9dragon/Desktop/copy.txt");

        //2. 创建输入流和输出流
        InputStream is = new FileInputStream(srcFile);
        OutputStream os = new FileOutputStream(desFile, false);
        //假设源文件内容为 "ABCDEFGHIJKL", 长度为12, 输出流的参数是false,  
        //而byte数组长度为10, 因此会循环两遍调用write方法进行写入操作,
        //那么不论循环多少次调用这个主方法, 拷贝的文件内容都是 "ABCDEFGHIJKL";
        //如果将参数改为true, 然后循环调用这个主方法,  
        //那么拷贝文件将会写入循环次数遍的"ABCDEFGHIJKL".
        //因此这个参数是针对不同输出流来判断是否需要续写或者覆盖, 相同输出流必然是进行续写.

        //3. 定义缓冲区和长度接收变量
        byte[] bytes = new byte[10];
        int len = 0;

        //4. 循环读取并写入数据到输出点
        while ((len = is.read(bytes)) > 0) {
            os.write(bytes, 0, len);
        }

        //5. 关闭所有流的资源
        is.close();
        os.close();
    }
}

1.4. 正确地关闭资源和处理异常

  • 低于 JAVA7 使用的资源关闭方法, 以上面的拷贝文件代码为例.

    public class Test {
      public static void main(String[] args){
          //0. 先定义输入输出流, 赋值为空, 为了后面关闭资源能进行调用这俩对象.
          InputStream is = null;
          OutputStream os = null;
          //1. 创建读取源和输出点
          File srcFile = new File("/Users/leon9dragon/Desktop/test.txt");
          File desFile = new File("/Users/leon9dragon/Desktop/copy.txt");
          try {
              //2. 创建输入流和输出流
              is = new FileInputStream(srcFile);
              os = new FileOutputStream(desFile, false);
              //3. 定义缓冲区和长度接收变量
              byte[] bytes = new byte[10];
              int len = 0;
              //4. 循环读取并写入数据到输出点
              while ((len = is.read(bytes)) > 0) os.write(bytes, 0, len);
          }
          catch (Exception e) {
              e.printStackTrace();
          }
          finally {
              //5. 关闭所有流的资源
              try {
                  is.close();
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  os.close();
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
    }
  • JAVA7 版本中存在新特性, 能够自动资源关闭, 可以让上述代码更简洁.

    public class Test {
      public static void main(String[] args){
          //1. 创建读取源和输出点
          File srcFile = new File("/Users/leon9dragon/Desktop/test.txt");
          File desFile = new File("/Users/leon9dragon/Desktop/copy.txt");
          try (
                  //2. 定义输入输出流.
                  InputStream is = new FileInputStream(srcFile);
                  OutputStream os = new FileOutputStream(desFile, false);
          ) {
              //3. 定义缓冲区和长度接收变量
              byte[] bytes = new byte[10];
              int len = 0;
              //4. 循环读取并写入数据到输出点
              while ((len = is.read(bytes)) > 0) os.write(bytes, 0, len);
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    }

1.5. FileReader 文件字符输入流

  • 如果使用文件字节输入流来读取字符, 有可能会出现乱码的情况.

  • 通常类似汉字等特殊字符根据编码基本是两个字节或以上的长度,
    而字节流是逐个字节读取的并逐个字节进行存放在缓冲区当中的,
    因此在读取这些字符的时候会把字符所组成的字节拆开逐个读取,
    从而造成获取的文件内容乱码或文本不对的情况出现.

  • 字符流是对字节流的一种补充, 先有字节流再出现的字符流.

1.5.1. 常用构造器
  • FileReader(File file)
    • 作用:
      在给定从中读取数据的 File 的情况下创建一个新 FileReader.
1.5.2. 常用方法
  • int read()

    • 作用:
      读取单个字符并返回字符所对应的整数, 读到末尾则返回 -1
  • int read(char[] cbuf)

    • 作用:
      将字符读入数组, 并返回存入数组的字符数量, 如果读到末尾不能再存入, 则返回 -1
  • abstract int read(char[] cbuf, int off, int len)

    • 作用:
      将字符读入数组的某一部分, 将 len 个字符存入字符数组当中, 从索引位 off 开始存.

1.6. FileWriter 文件字符输出流

1.6.1. 常用构造器
  • FileWriter(File file)

    • 作用:
      根据给定的 File 对象构造一个 FileWriter 对象
  • FileWriter(File file, boolean append)

    • 作用:
      根据给定的 File 对象构造一个 FileWriter 对象
1.6.2. 常用写入方法
  • void write(char[] cbuf)

    • 作用:
      写入字符数组
  • abstract void write(char[] cbuf, int off, int len)

    • 作用:
      写入字符数组的某一部分, 写入从数组索引位 off 开始的 len 个字符
  • void write(int c)

    • 作用:
      写入单个字符
  • void write(String str)

    • 作用:
      写入字符串
1.6.3. flush(刷新)操作
  • 输出流中都有 fush 方法, 该方法用于刷新输出流的缓冲区.

  • 缓冲区的必要性:

    • 计算机访问外部设备(磁盘文件), 要比直接访问内存慢很多,
      如果每次 write 都要直接写出到磁盘文件中, CPU 都会花更多的时间.
    • 此时我们可以准备一个内存缓冲区程序, 每次 write 方法都是直接写到内存缓冲区中,
      当内存缓冲区满后, 系统才把缓冲区内容一次性写出给磁盘文件.
  • 缓冲区的好处:

    • 提高 CPU 使用率.
    • 有机会回滚写入的数据.
  • 缓冲区的备注:

    • 对于字节流, flush 方法不是都有作用, 但是对于字符流来说都起作用.

    • 在使用字符输出流时, 若调用了 close 方法, 系统会在关闭资源前先调用 flush 方法.

    • 因此不需手动取调用该方法刷新缓冲区, 系统会自动调用, 前提是先调用 close 方法.

    • 如果在使用字符输出流时没有调用 close 方法关闭资源,
      也没有手动调用 flush 方法刷新缓冲区, 那么将会导致文件写入失败,
      生成文件是空文件, 因此无论是什么流, 一定要调用 close 方法及时关闭资源.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值