字节与字符流 初识

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战

Reader、Writer

字符输入流 Reader

  • Reader() :创建一个新的字符流阅读器,其关键部分将在阅读器本身上同步
  • read() :读一个字符
  • read(char[] cbuf) :将字符读入数组
  • read(char[] cbuf, int off, int len) :将字符读入数组的一部分

字符输出流 Writer

  • Writer() :创建一个新的字符流编写器,其关键部分将在编写器本身上同步
  • append(char c) :将指定的字符追加到此writer
  • append(CharSequence csq) :将指定的字符序列追加到此writer
  • append(CharSequence csq, int start, int end) :将指定字符序列的子序列追加到此writer
  • write(char[] cbuf) :写一个字符数组
  • write(String str) :写一个字符串
  • write(String str, int off, int len) :写一个字符串的一部分
  • flush() :刷新流

InputStreamReader、OutputStreamWriter

InputStreamReader、OutputStreamWriter 可以将字节流转换为字符流,同时指定字符编码

try {
    InputStreamReader fileReader = new InputStreamReader(new FileInputStream("D:/测试用例.txt"));
    OutputStreamWriter fileWriter = new OutputStreamWriter(new FileOutputStream("D:/测试用例-12.txt"), StandardCharsets.UTF_8);
    int data;
    while ((data = fileReader.read()) != -1) {
        System.out.print((char) data);
        fileWriter.write((char) data);
    }
    fileWriter.close();
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

FileReader、FileWriter

文件字符流 FileReaderFileWriter

FileReader、FileWriter 值得注意的点是,只可以使用默认的字符编码,不可指定

FileReader 读取示例:

try {
    FileReader fileReader = new FileReader("D:/测试用例.txt");
    int data;
    while ((data = fileReader.read()) != -1) {
        System.out.print((char) data);
    }
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

FileWriter 输入示例:

try {
    FileWriter fileWriter = new FileWriter("D:/测试用例.txt", true);
    fileWriter.write("追加的字符串");
    fileWriter.flush();
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

FileReaderFileWriter 文件字符流 实现文件拷贝

try {
    FileReader fileReader = new FileReader("D:/测试用例.txt");
    FileWriter fileWriter = new FileWriter("D:/测试用例-12.txt", true);
    int data;
    while ((data = fileReader.read()) != -1) {
        System.out.print((char) data);
        fileWriter.write((char) data);
    }
    fileWriter.close();
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

CharArrayReader、CharArrayWriter

CharArrayReader、CharArrayWriter 与字节数组字节流类似,不同的是的,单位是 char 字符,且可以指定字符编码

try {
    InputStreamReader fileReader = new InputStreamReader(new FileInputStream("D:\\测试用例.txt"), StandardCharsets.UTF_8);
    CharArrayWriter charArrayWriter = new CharArrayWriter();
    int data;
    char[] chars = new char[1024];
    while ((data = fileReader.read(chars)) != -1) {
        charArrayWriter.write(chars, 0, data);
    }
    System.out.println(charArrayWriter.toString());
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

StringReader、StringWriter

StringReader、StringWriter 与 CharArrayReader、CharArrayWriter 类似,只是采用了 String、StringBuffer 替代了 char 数组,当然,本质上是一样的

try {
    InputStreamReader fileReader = new InputStreamReader(new FileInputStream("D:\\测试用例.txt"), StandardCharsets.UTF_8);
    StringWriter stringWriter = new StringWriter();
    int data;
    char[] chars = new char[1024];
    while ((data = fileReader.read(chars)) != -1) {
        stringWriter.write(chars, 0, data);
    }
    System.out.println(stringWriter.toString());
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

BufferedReader、BufferedWriter

同样是缓冲流,字符缓冲流的使用也需要注意刷新缓冲区

try {
    FileReader fileReader = new FileReader("D:\\测试用例.txt");
    BufferedReader bufferedReader = new BufferedReader(fileReader);
    int data;
    char[] chars = new char[1024];
    while ((data = bufferedReader.read(chars)) != -1) {
        System.out.println(new String(chars, 0, data));
    }
} catch (IOException e) {
    e.printStackTrace();
}
try {
    FileWriter fileWriter = new FileWriter("D:\\测试用例.txt");
    BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
    bufferedWriter.write("这是字符缓冲流,注意刷新缓冲区");
    bufferedWriter.flush();
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

PrintWriter

try {
    PrintWriter printWriter = new PrintWriter("D:\\测试用例.txt", StandardCharsets.UTF_8);
    printWriter.write("这是打印输出流");
    printWriter.write("可以直接写入参\n数名,也支持缓冲区");
    // 字符打印输出流是需要手动刷新缓冲区的
    printWriter.flush();
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

标准流

Scanner 类是独自存在的一个Java类,它可以接收IO流数据,并解析输出

通常清空下,Scanner是接收的标准流,在Java的使用,System.out 就是标准流的一种,标准输出流

与之对应的是标准输入流,System.in ,常见的用法为:接收输入的数据

Scanner scanner = new Scanner(System.in);
System.out.println("接收标准输入流,自键盘传入");
String str = scanner.next();
System.out.println(str);
复制代码

InputStream、OutputStream

字节输入流 InputStream

  • read() :从输入流读取数据的下一个字节,若读取至末尾,则返回 -1
  • read(byte[] b) :从输入流读取任意字节,并将它们存储到缓冲区
  • read(byte[] b, int off, int len) :读取 len 字节的数据到一个字节数组
  • close() :关闭输入流,并释放资源

字节输出流 OutputStream

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

InputStream、OutputStream 作为字节流的抽象父类存在,具体的实现请看之后

FileInputStream、FileOutputStream

文件字节流 FileInputStreamOutputStream

文件字节流是 节点流,可以直接读、写数据

FileOutputStream :写入数据到硬盘中

单字节写入

try {
    FileOutputStream stream = new FileOutputStream("D:/测试用例.txt");
    // main 程序的一次运行视为一次数据的写入
    stream.write('H');
    stream.write('W');
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

多字节写入,以字节数组的形式

try {
    FileOutputStream stream = new FileOutputStream("D:/测试用例.txt");
    String str = "多个文字组成的字符串";
    // 将字符串转化为字节数组
    byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
    stream.write(bytes);
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

单字节读取

try {
    FileInputStream stream = new FileInputStream("D:/测试用例.txt");
    int data;
    while ((data = stream.read()) != -1) {
        // 将整数再转换为对应的字符
        System.out.print((char) data);
    }
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

多字节读取

try {
    FileInputStream stream = new FileInputStream("D:/测试用例.txt");
    int data;
    byte[] bytes = new byte[1024];
    while ((data = stream.read(bytes)) != -1) {
        // 输入出字符,一次最多输出 1024
        System.out.print(new String(bytes, 0, data));
    }
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

文件字节流,实现文件拷贝

try {
    FileInputStream inputStream = new FileInputStream("D:/测试用例.txt");
    FileOutputStream outputStream = new FileOutputStream("D:/测试用例-12.txt");
    int data;
    byte[] bytes = new byte[1024 * 4];
    while ((data = inputStream.read(bytes)) != -1) {
        // 写入数据的同时,打印内容
        System.out.print(new String(bytes, 0, data));
        outputStream.write(bytes, 0, data);
    }
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

ByteArrayInputStream、ByteArrayOutputStream

在之前的文件字节流中,存在一个问题,不能够一次性读取完文件数据

简单的理解,若一个字符正好是被分成了两个字节数组,则输出的时候就可能出现乱码

ByteArrayInputStream、ByteArrayOutputStream,则是为文件字节流设置了一个缓冲,可以存储每次读取的数据,最后一次输出完毕

try {
    FileInputStream inputStream = new FileInputStream("D:\\测试用例.txt");
    ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
    byte[] bytes = new byte[1024];
    int len;
    while ((len = inputStream.read(bytes)) != -1) {
        // 存储文件字节流读取的数据
        arrayOutputStream.write(bytes, 0, len);
    }
    // 最后读取完毕时,一次输出
    System.out.println(arrayOutputStream.toString(StandardCharsets.UTF_8));
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

DateInputStream、DateOutputStream

之前的字节流中,只是以字节为单位,进行数据的读出、写入

DateInputStream、DateOutputStream 则是支持以不同的类型读出、写入,例如 int、long,设置是自定义的 Java 类

这里,一个 Java 类的实例对象为例,读出写入的方式

Person 类

public class Person {
    private String name;
    private Integer age;

    public Person() {
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
复制代码

将 Java 类对象写入到硬盘中

Person person = new Person("里斯", 23);
try {
    FileOutputStream outputStream = new FileOutputStream("D:\\测试用例.txt");
    DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
    dataOutputStream.writeUTF(person.getName());
    dataOutputStream.writeInt(person.getAge());
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

将 Java 类对象读取到内存中

try {
    FileInputStream inputStream = new FileInputStream("D:\\测试用例.txt");
    DataInputStream dataInputStream = new DataInputStream(inputStream);
    String name = dataInputStream.readUTF();
    int age = dataInputStream.readInt();
    System.out.println(name);
    System.out.println(age);
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

可以很清晰的看到,字节流不仅仅是使用字节为单位进行传输的,也可以直接写入、读出 Java 类

这里面涉及到了Java对象的序列化机制,可以再被写入数据的文件中发现,文字是以乱码的形式表现的

BufferedInputStream、BufferedOutputStream

BufferedInputStream、BufferedOutputStream 是字节缓冲输入流,可以为字节读出、写入提供缓冲区的支持

简单的理解,由字节缓冲流包装的文件字节流,可以将读出、写入的数据暂时缓存,最后再输出、写入

可能会觉得,缓冲字节流与字节数组流相似,但字节数组流内部并不存在缓冲区,只是功能效果上的类似

try {
    FileInputStream inputStream = new FileInputStream("D:/测试用例.txt");
    FileOutputStream outputStream = new FileOutputStream("D:/测试用例-12.txt", true);
    BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
    int data;
    byte[] bytes = new byte[1024];
    while ((data = bufferedInputStream.read(bytes)) != -1) {
        System.out.print(new String(bytes, 0, data));
        bufferedOutputStream.write(bytes, 0, data);
    }
    // 刷新缓冲区,否则数据不会被写入到硬盘中
    bufferedOutputStream.flush();
} catch (IOException e) {
    e.printStackTrace();
}
复制代码

值得注意的是,当数据写入到硬盘中时,必须刷新缓冲区 flush()

缓冲字节流的存在,使得无须频繁的读出、写入,但缓冲字节流无法直接操作数据,它是处理流,而非节点流

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享