• Java IO---字节流和字符流


    一.IO流简介

    1. 流是一个抽象概念,Java程序和外部设备(可以是硬盘上的文件,也可以是网络设备)之间的输入输出操作是基于流的。

      流就好比水管中的水流,具有流入和流出,类比数据的输入和输出。

      Java中流屏蔽了实际IO设备中处理数据的细节,主要用来处理设备之间的数据传输。

    2. IO流的分类

      根据数据的流向可以分为:输入流和输出流

      根据数据的类型可以分为:字节流和字符流

      Java中将所有流类型都放在了java.io包中,用于实现输入和输出功能,相关类关系如下图

      04IO流.png

    二.字节流

    1. 一切皆为字节

      Java中的字节流 处理的基本单位是单个字节,一切的文件数据(文本,图片,视频等)在存储时,都是以二进制的形式保存的,都是一个字节一个字节的,传输时候也一样,所以字节流可以传输任意文件数据,所以在操作流的时候应该明确,无论使用什么流对象,其底层传输始终是二进制数据。

    2. 字节输出流(OutputStream)

      public abstract class OutputStream是字节输出流的所有类的超类,是一个抽象类,所有的字节输出流都是继承自OutputStream。

      它定义了一些共用的方法:

      • void close():关闭此输出流并释放与此流有关的所有系统资源
      • void flush():刷新此输出流并强制写出所有缓冲的输出字节。
      • void write(byte[] b):将 b.length 个字节从指定的 byte 数组写入此输出流。
      • void write(byte[] b, int off, int len):将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
      • abstract void write(int b):将指定的字节写入此输出流。

      OutputStream有很多子类,这里主要学习FileOutputStream,FileOutputStream是文件输出流,用于将数据写到文件。

      构造方法是:

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

      • FileOutputStream(File file, boolean append):创建一个向指定File对象表示的文件中写入数据的文件输出流,其中参数append代表是否追加写入文件

      • FileOutputStream(String name):创建一个向具有指定名称的文件中写入数据的输出文件流。

      • FileOutputStream(String name, boolean append):创建一个向具有指定name的文件中写入数据的输出文件流,其中参数append代表是否追加写入文件

        注意:

        当创建一个流对象时,需要传入一个文件路径,在该路径下,如果没有这个文件,则会创建该文件,如果有这个文件,将会清空这个文件的数据再写入(带append参数,且append为true的则不会清空,因为它代表着追加内容)

      示例:

      import java.io.FileOutputStream;
      import java.io.IOException;
      
      public class TestFileOutputStream {
          public static void main(String[] args) throws IOException{
              // 1. 创建FileOutputStream对象
              FileOutputStream fos = new FileOutputStream("File\\a.txt");
      
              // 写入一个字节  97 就是字符 'a'
              fos.write(97);  
      
              // 写入字节数组
              byte[] bytes = "你好".getBytes();
              fos.write(bytes);
      
              // 写入字节数组,指定长度
              byte[] b = "abcdef".getBytes();
              fos.write(b, 2, 3);     // 从第二位开始,写3个,就是c,d,e
      
              fos.close();
      
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23

      运行结果:

      生成文件a.txt,内容为:a你好cde

      注意:

      write调用时候是不会写换行的。如果需要换行,需要自己写入。

      不同系统的换行不同:

      Windows是\r\n

      Unix是\n

    3. 字节输入流(InputStream)

      public abstract class InputStream是字节输入流的所有类的超累,是一个抽象类,他定义了一些共用方法:

      • void close():关闭此输入流并释放与该流关联的所有系统资源。
      • abstract int read():从输入流中读取数据的下一个字节。
      • int read(byte[] b):从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中,返回值为每次读取到的有效字节个数。

      InputStream有很多子类,这里主要学习FileInputStream,FileInputStream是文件输入流,用于从文件中读取字节。

      构造方法是:

      • FileInputStream(File file):过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
      • FileInputStream(String name):通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

      注意:

      当创建一个输入流对象时,必须传入一个文件路径,在改路径下,如果没有该文件的话,会报FileNotFoundException

      示例:文件b.txt内容为:abcdefg

      使用int read()读取:

      import java.io.FileInputStream;
      import java.io.IOException;
      
      public class TestFileInputStream {
          public static void main(String[] args) throws IOException {
              FileInputStream fis = new FileInputStream("File\\b.txt");
      
              // 读取一个字节
              // System.out.println(fis.read()); // 97
              // System.out.println(fis.read()); // 98
              // System.out.println(fis.read()); // 99
              // System.out.println(fis.read()); // 100
              // System.out.println(fis.read()); // 101
              // System.out.println(fis.read()); // 102
              // System.out.println(fis.read()); // 103
              // System.out.println(fis.read()); // -1
      
              // 使用循环读
              int r;
              while ((r = fis.read()) != -1) {
                  System.out.print((char)r);
              }   // 输出 abcdefg
      
              fis.close();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26

      说明:

      1. 示例使用每次读取一个字节的方式从文件中读取数据,其中要注意虽然每次读取了一个自己,但是会被自动提升为int类型,所以打印出来的是97,98,99,100,101,102,103,这些是字符对应的ASCII码,当使用强制转换为char之后,就正常输出abcdefg了。
      2. 当读取到文件末尾时候,读出来的是-1

      使用int read(byte[] b)读取:

      import java.io.FileInputStream;
      import java.io.IOException;
      
      public class TestFileInputStream {
          public static void main(String[] args) throws IOException {
              FileInputStream fis = new FileInputStream("File\\b.txt");
      
              // 读取到数组
              byte[] b = new byte[3];
              int len;
      
              while ((len = fis.read(b)) != -1) {
                  System.out.print(new String(b) + " ");
              }
      
              fis.close();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

      说明:

      1. 程序输出的结果为:abc def gef
      2. 这种输出结果是错误的,正确应该是 abc def g,因为在第三次读取时候,文件中只读取了一个字节g,然后存在了数组的第一位,因为第二次时候数组内容为def,所以第三次时候,数组的内容为gef,只改变了第一位
      3. 要修正上面的问题,要使用len,获取有效的字节。

      修正代码:

      import java.io.FileInputStream;
      import java.io.IOException;
      
      public class TestFileInputStream {
          public static void main(String[] args) throws IOException {
              FileInputStream fis = new FileInputStream("File\\b.txt");
      
              // 读取到数组
              byte[] b = new byte[3];
              int len;
      
              while ((len = fis.read(b)) != -1) {
                  System.out.print(new String(b, 0, len) + " " ); 
              }
      
              fis.close();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

    三.字符流

    ​ 当使用字节流读取文本本间时,可能会存在一个小问题,就是遇到中文字符时候,可能不会显示完整的字符,因为一个中文字符可能占用多个字符存储,所以Java中提供了字符流类,以字符为单位读写数据。

    1. 字符输出流(Writer)

      public abstract class Writer是用于写出字符流的所有类的超类,是一个抽象类,它定义了字节输出流的基本共性能方法:

      • void write(int c):写入单个字符。
      • void write(char[] cbuf):写入字符数组。
      • abstract void write(char[] cbuf, int off, int len):写入字符数组的某一部分。
      • void write(String str): 写入字符串。
      • void write(String str, int off, int len):写入字符串的某一部分。
      • void flush():刷新该流的缓冲。
      • void close():关闭此流,但要先刷新它。

      FileWriter类是写字符到文件的类,构造时使用系统默认的字符编码和默认字节缓冲区。

      构造方法:

      • FileWriter(File file):根据给定的 File 对象构造一个 FileWriter 对象。
      • FileWriter(File file, boolean append):根据给定的 File 对象构造一个 FileWriter 对象。其中append参数代表是否追加。
      • FileWriter(String fileName):根据给定的文件名构造一个 FileWriter 对象。
      • FileWriter(String fileName, boolean append):根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。

      基本写数据示例:

      import java.io.FileWriter;
      import java.io.IOException;
      
      public class TestFileWriter {
          public static void main(String[] args) throws IOException{
              FileWriter fw = new FileWriter("File\\c.txt");
      
              fw.write(97);   // 写第一个字符
              fw.write('b');  // 写第二个字符
              fw.write('c');  // 写第三个字符
      
              fw.close();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

      注意:

      1. 如果没有调用close方法,写入的数据只是保存到了缓冲区,并没有写到文件中,这一点和FileOutputStream不一样,或者写完调用**flush()**刷新缓冲区。
      2. 最后如果调用了close(),可以不调用flush()刷新,因为close()会先刷新缓冲,再关闭流。
      3. 即使使用了flush()刷新了缓冲,写出了数据,但最后还是要调用close()方法来释放资源。

      其他方法写数据示例:用法和FileOutputStream类似

      import java.io.FileWriter;
      import java.io.IOException;
      
      public class TestFileWriter {
          public static void main(String[] args) throws IOException{
              FileWriter fw = new FileWriter("File\\c.txt");
      
              // 写字符数组
              char[] chars = "我是中国人".toCharArray();
              fw.write(chars);
      
              // 从索引1开始,写2个字节
              fw.write(chars, 1, 2);
      
              // 写字符串
              String str = "你好吗";
      
              fw.write(str);
      
              fw.write(str, 1, 1);
      
              fw.close();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24

      注意:

      字符流只能操作文本文件,不能操作图片,视频等非文本文件(因为是二进制存储的,要用字节流)

  • 相关阅读:
    VB.NET 三层登录系统实战:从设计到部署全流程详解
    Okhttp 403 Forbidden
    YOLOv2-yolo9000-batter,faster,stronger 论文精度
    【ARM 嵌入式 编译系列 11.3 -- GCC attribute packed noreturn constructor 介绍】
    9月17日计算机视觉基础学习笔记——认识机器学习
    react高阶成分(HOC)实践例子
    SSM巢湖学院校园报修系统毕业设计-附源码021813
    NPU上PyTorch模型训练问题案例
    [C++](26)智能指针
    本宁顿学院青年作家奖报名备赛
  • 原文地址:https://blog.csdn.net/chisuisi5702/article/details/126284583