• 五、04【Java IO模型】之字符流


    Reader介绍

    public abstract class Reader implements Readable, Closeable {}

    用于读取字符流的抽象类。子类必须实现的唯一方法是read(char[],int,int)和close()。

    属性&方法

    1. // 用于同步此流上的操作的对象。为了提高效率,字符流对象可以使用自身以外的对象来保护关键部分。
    2. // 因此,子类应该使用此字段中的对象,而不是此字段或同步方法。
    3. protected Object lock;
    4. // 读取单个字符。此方法将阻塞,直到出现可用的字符、发生I/O错误或到达流的末尾。
    5. public int read() throws IOException {}
    6. // 将字符读入数组。此方法将阻塞,直到有可用的输入、发生I/O错误或到达流的末尾。
    7. public int read(char[] cbuf) throws IOException{}
    8. // 关闭流并释放与之关联的任何系统资源。一旦流被关闭,进一步的read()、ready()、mark()、reset()或skip()调用将抛出一个IOException。
    9. // 关闭之前关闭的流没有任何效果
    10. public abstract void close() throws IOException{}
    11. // 跳过字符。此方法将阻塞,直到有可用的字符、发生I/O错误或到达流的末尾。
    12. public long skip(long n) throws IOException{}

    子类体系结构图

    Writer介绍 

    public abstract class Writer implements Appendable, Closeable, Flushable {}

    用于写入字符流的抽象类。子类必须实现的唯一方法是write(char[]、int、int)、flush()和close()。

    属性&方法

    1. // 用于存放字符串和单个字符写操作的临时缓冲区
    2. private char[] writeBuffer;
    3. // 写入缓冲大小,必须大于1
    4. private static final int WRITE_BUFFER_SIZE = 1024;
    5. // 用于同步此流上的操作的对象。为了提高效率,字符流对象可以使用自身以外的对象来保护关键部分。
    6. // 因此,子类应该使用此字段中的对象,而不是此字段或同步方法。
    7. protected Object lock;
    8. // 写入不同类型
    9. public void write(int c) throws IOException{}
    10. public void write(char[] cbuf) throws IOException{}
    11. public void write(String str) throws IOException{}
    12. // 写入字符串指定内容
    13. public void write(String str, int off, int len){}
    14. // 刷新流。如果流在缓冲区中保存了来自各种write()方法的任何字符,则立即将其写入预期的目的地。
    15. // 然后,如果目的地是另一个字符流或字节流,则刷新它。
    16. // 因此,一次flush()调用将刷新writer和OutputStreams链中的所有缓冲区。
    17. public abstract void flush() throws IOException{}
    18. // 关闭流,先flush它。一旦流被关闭,进一步的write()或flush()调用将导致抛出IOException。关闭之前关闭的流没有任何效果。
    19. public abstract void close() throws IOException{}
    20. ... ...

    子类体系结构图

    Reader&Writer子类 

    大多数子类将重写两个抽象类定义的一些方法,以提供更高的效率或附加功能。

    BufferedReader&BufferedWriter

    为了提高字符流读写的效率,引入了缓冲机制,进行字符批量的读写,提高单个字符读写的效率。

    BufferedReader用于加快读取字符的速度,BufferedWriter用于加快写入的速度;

    BufferedReader 从字符输入流读取文本,缓冲字符,以便高效读取字符、数组和行。可以指定缓冲区大小,也可以使用默认大小。默认值对于大多数情况都够用。

    通常,读取器发出的每个读取请求都会导致底层字符或字节流发出相应的读取请求。因此,建议将BufferedReader包装在任何read()操作可能代价高昂的读取器周围,例如FileReaders和InputStreamReaders。例如:BufferedReader in = new BufferedReader(new FileReader("foo.in"));

    将缓冲指定文件中的输入。在没有缓冲的情况下,每次调用read()或readLine()都可能导致从文件中读取字节,转换为字符,然后返回,这可能非常低效。

    使用DataInputStreams进行文本输入的程序可以通过使用适当的BufferedReader替换每个DataInputStream来本地化。

    属性&构造函数

    1. // 读对象标识
    2. private Reader in;
    3. // 字符数组
    4. private char cb[];
    5. // 默认字符缓冲大小
    6. private static int defaultCharBufferSize = 8192;
    7. // 默认期望行长度
    8. private static int defaultExpectedLineLength = 80;
    9. // 创建一个使用默认大小的输入缓冲区的缓冲字符输入流。
    10. public BufferedReader(Reader in) {}
    11. // 创建一个使用指定大小的输入缓冲区的缓冲字符输入流。
    12. public BufferedReader(Reader in, int sz) {}
    13. ... ...

    BufferedWriter 将文本写入字符输出流,缓冲字符,以便高效地写入单个字符、数组和字符串。可以指定缓冲区大小,也可以接受默认大小。默认值对于大多数情况都够用。通常,写入程序会立即将其输出发送到底层字符或字节流。除非需要提示输出,否则建议将BufferedWriter包装在任何 write() 操作可能代价高昂的Writer周围,例如FileWriter和OutputStreamWriter。例如:PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));

    将PrintWriter的输出缓冲到文件中。如果没有缓冲,每次调用 print() 方法都会导致字符转换为字节,然后立即写入文件,这效率非常低。

    属性&构造函数

    1. // 写出对象标识
    2. private Writer out;
    3. // 字符数组
    4. private char cb[];
    5. // 默认字符缓冲大小
    6. private static int defaultCharBufferSize = 8192;
    7. // 创建一个使用默认大小的输出缓冲区的缓冲字符输出流。
    8. public BufferedWriter(Writer out) {}
    9. // 创建一个使用给定大小的输出缓冲区的新的缓冲字符输出流
    10. public BufferedWriter(Writer out, int sz) {}
    11. ... ...

    代码示例

    见下

    CharArrayReader&CharArrayWriter

    字符数组输入输出流

    CharArrayReader 这个类实现了一个可以用作字符输入流的字符缓冲区。

    属性&构造函数

    1. // 字符缓冲数组
    2. protected char buf[];
    3. // 当前缓冲位置
    4. protected int pos;
    5. // 标记缓冲位置
    6. protected int markedPos = 0;
    7. // 缓冲区末端的索引。
    8. protected int count;
    9. // 从指定的字符数组创建一个CharArrayReader
    10. public CharArrayReader(char buf[]) {}
    11. // 从指定的字符数组创建一个CharArrayReader.
    12. // 生成的读取器将在给定的偏移量处开始读取。可以从该读取器读取的字符值总数将为length或buf。长度偏移,以较小者为准。
    13. public CharArrayReader(char buf[], int offset, int length) {}
    14. ... ...

    CharArrayWriter 类实现了一个可以用作编写器的字符缓冲区。当数据写入流时,缓冲区会自动增长。可以使用 ToCharray() 和 toString() 检索数据。但是在这个类上调用 close() 没有效果,在流关闭后可以调用这个类的方法,而不会生成IOException。

    属性&构造函数

    1. // 字符缓冲数组
    2. protected char buf[];
    3. // 缓冲区末端的索引。
    4. protected int count;
    5. // 创建一个新的CharArrayWriter。默认长度32
    6. public CharArrayWriter(char buf[]) {}
    7. // 使用指定的初始大小创建一个新的CharArrayWriter
    8. public CharArrayWriter(int initialSize) {}
    9. ... ...

    代码示例

    1. private static void charArrayRWTest(){
    2. try {
    3. String str = "Hello World";
    4. // 创建字符数组输入流
    5. CharArrayReader reader = new CharArrayReader(str.toCharArray());
    6. // 从字符数组输入流读取字符
    7. char[] chars = new char[1024];
    8. int len = reader.read(chars);
    9. System.out.println(new String(chars, 0, len));
    10. reader.close();
    11. // 创建字符数组输出流
    12. CharArrayWriter writer = new CharArrayWriter();
    13. // 将字符串写入到字符数组输出流
    14. String str2 = "Hello World 2022";
    15. writer.write(str2.toCharArray());
    16. System.out.println(writer.toString());
    17. writer.close();
    18. } catch (IOException e) {
    19. e.printStackTrace();
    20. }
    21. }

    InputStreamReader&OutputStreamWriter

    转换输入输出流

    InputStreamReader是字节流和字符流之间的桥梁。它读取字节并使用指定的字符集将其解码为字符。它使用的字符集可以通过名称指定,也可以显式给出,或者可以接受平台的默认字符集。每次调用InputStreamReader的read()方法都可能导致从底层的字节输入流读取一个或多个字节。为了有效地将字节转换为字符,可能会提前从底层流读取比满足当前读取操作所需的字节更多的字节。

    为了提高效率,考虑将InputStreamReader包装到BufferedReader中。例如: BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

    属性&构造函数

    1. // 流解码器
    2. private final StreamDecoder sd;
    3. // 创建一个使用默认字符集的InputStreamReader。
    4. public InputStreamReader(InputStream in) {}
    5. // 创建一个指定字符集的InputStreamReader。
    6. public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException {}
    7. // 创建一个使用给定字符集解码器的InputStreamReader。
    8. public InputStreamReader(InputStream in, CharsetDecoder dec) {}
    9. // 创建一个使用给定字符集的InputStreamReader。
    10. public InputStreamReader(InputStream in, Charset cs) {}

    OutputStreamWriter是从字符流到字节流的桥梁:写入其中的字符使用指定的字符集编码为字节。它使用的字符集可以通过名称指定,也可以显式指定,或者可以接受平台的默认字符集。每次调用write()方法都会导致对给定字符调用编码转换器。在写入底层输出流之前,生成的字节会累积在缓冲区中。可以指定此缓冲区的大小,但默认情况下,它对于大多数用途来说都足够大。请注意,传递给write()方法的字符不会被缓冲。

    为了提高效率,可以考虑在BufferedWriter中包装OutputStreamWriter,以避免频繁调用转换器。例如:

    Writer out = new BufferedWriter(new OutputStreamWriter(System.out));

    这个类总是用字符集的默认替换序列替换不正常的代理元素和不可映射的字符序列。当需要对编码过程进行更多控制时,应该使用CharsetEncoder类。

    属性&构造函数

    1. // 流编码器
    2. private final StreamDecoder sd;
    3. // 创建一个使用默认字符编码的OutputStreamWriter。
    4. public OutputStreamWriter(OutputStream out) {}
    5. // 创建一个使用给定字符集的OutputStreamWriter。
    6. public OutputStreamWriter(OutputStream out, String charsetName){}
    7. // 创建一个使用给定字符集编码器的OutputStreamWriter。
    8. public OutputStreamWriter(OutputStream out, CharsetEncoder enc) {}
    9. // 创建一个使用命名字符集的OutputStreamWriter。
    10. public OutputStreamWriter(OutputStream out, Charset cs) {}

    代码示例

    1. private static void ioStreamRWTest() {
    2. try {
    3. // 从文件中获得输入字节到字节输入流
    4. FileInputStream fis = new FileInputStream("/aaa.txt");
    5. // 为FileInputStream加上字符处理功能,将字节流转换成字符流。
    6. // 本文件如果有中文,中文则会乱码,需要设置编码格式。
    7. InputStreamReader isr = new InputStreamReader(fis, "GBK");
    8. // 提高读取效率,可以使用 BufferedReader 包装 InputStreamReader
    9. BufferedReader br = new BufferedReader(isr);
    10. // 创建将数据写入到文本文件的文件输出流
    11. FileOutputStream fos = new FileOutputStream("/bbb.txt");
    12. // 为FileOutputStream加上字符处理功能
    13. OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");
    14. // 提高写入效率,可以使用 BufferedWriter 包装 OutputStreamWriter 中,以避免频繁调用转换器
    15. BufferedWriter bw = new BufferedWriter(osw);
    16. // // 一个字符一个字符的读
    17. // int read = br.read();
    18. // // 读到末尾 read 是 -1
    19. // while(read != -1) {
    20. // System.out.print((char)read);
    21. // // 一个字符一个字符的写
    22. // bw.write(read);
    23. // // 继续读下一个字符
    24. // read = br.read();
    25. // }
    26. // 一行一行的读
    27. String line;
    28. // 读到末尾 line 是 null
    29. while ((line = br.readLine()) != null){
    30. System.out.println(line);
    31. // 一行一行的写
    32. bw.write(line);
    33. // 写完一行,起一行新的
    34. bw.newLine();
    35. }
    36. // 强制刷新缓冲区
    37. osw.flush();
    38. // 关闭流
    39. bw.close();
    40. osw.close();
    41. fos.close();
    42. br.close();
    43. isr.close();
    44. fis.close();
    45. } catch (IOException e) {
    46. e.printStackTrace();
    47. }
    48. }

    使用场景

    1)做一些文件复制

    2)

    FileReader&FileWriter

    FileReader用于读取字符文件的方便类。此类的构造函数假定默认字符编码和默认字节缓冲区大小是合适的。

    若要自己指定这些值,可以在 FileInputStream 上构造一个 InputStreamReader。

    FileReader用于读取字符流。若要读取原始字节流,可以考虑使用FileInputStream。

    用于写入字符文件的方便类。此类的构造函数假定默认字符编码和默认字节缓冲区大小是可接受的。

    若要自己指定这些值,可以在 FileOutputStream 上构造一个 OutputStreamWriter。

    文件是否可用或是否可以创建取决于底层平台。尤其是某些平台,一次只允许一个FileWriter(或其他文件写入对象)打开一个文件进行写入。

    在这种情况下,如果涉及的文件已经打开,这个类中的构造函数将失败。

    FileWriter用于编写字符流。若要写入原始字节流,请考虑使用FileOutputStream。

    FileReader和FileWriter是使用默认编码格式通过字符方式操作文件的(中文会乱码),如果有中文可以使用InputStreamReader或OutputStreamWriter。

    代码示例

    1. private static void fileRWTest() {
    2. try {
    3. FileReader fr = new FileReader("/aaa.txt");
    4. FileWriter fw = new FileWriter("/bbb.txt");
    5. int read = fr.read();
    6. while (read != -1){
    7. System.out.print((char) read);
    8. fw.write(read);
    9. read = fr.read();
    10. }
    11. fw.flush();
    12. fw.close();
    13. fr.close();
    14. } catch (IOException e) {
    15. e.printStackTrace();
    16. }
    17. }

    PipedReader&PipedWriter

    管道字符输入流和管道字符输出流;

    与字节里的管道输入输出流一样;管道输入流提供写入管道输出流的任何数据字节。通常,一个线程从 PipedReader 对象读取数据,另一个线程将数据写入相应的PipedWriter。不建议尝试从单个线程同时使用这两个对象,因为这可能会使线程死锁。管道输入流包含一个缓冲区,在一定范围内将读操作与写操作分离。如果向连接的管道输出流提供数据字节的线程不再活动,则称管道已断开。

    属性&构造函数

    1. // 创建一个尚未连接的管道输入流
    2. public PipedReader() {}
    3. // 创建一个尚未连接的管道输入流,并为管道的缓冲区使用指定的管道大小。
    4. public PipedReader(int pipeSize){}
    5. // 创建一个管道输入流,使其直接连接到管道输出流。
    6. public PipedReader(PipedWriter src){}
    7. // 创建一个管道输入流,使其直接连接到管道输出流。并为管道的缓冲区使用指定的管道大小。
    8. public PipedReader(PipedWriter src, int pipeSize){}
    9. // PipedReader里的方法,也就那几个:read() close() ready();多一个connect(PipedWriter src)方法,进行连接管道输出流
    10. // 创建尚未连接到管道读取器的管道写入器。
    11. public PipedWriter(){}
    12. // 创建一个连接到指定的管道读取器的管道写入器。
    13. public PipedWriter(PipedReader snk){}
    14. // PipedWriter里的方法,同样也是那几个:write() flush() close();多一个connect(PipedReader snk)方法,进行连接管道输入流

    代码示例

    1. private static void pipedRW() {
    2. try {
    3. // 第一种:只能一方连向一方
    4. PipedReader pipedReader = new PipedReader();
    5. PipedWriter pipedWriter = new PipedWriter();
    6. pipedReader.connect(pipedWriter);
    7. pipedWriter.connect(pipedReader);
    8. // 第二种:只能一方连向一方
    9. PipedReader pipedReader2 = new PipedReader(new PipedWriter());
    10. PipedWriter pipedWriter2 = new PipedWriter(new PipedReader());
    11. } catch (IOException e) {
    12. e.printStackTrace();
    13. }
    14. }

    StringReader&StringWriter

    StringReader 是数据源为字符串的字符流;

    StringWirter可以在字符串缓冲区中收集其输出的字符流,然后可用于构造字符串。关闭 StringWriter 没有任何作用。

    这个类中的方法可以在流关闭后被调用而不产生IOException。

    属性&构造函数&方法

    1. // 创建一个新的字符串读取器。
    2. public StringReader(String s){}
    3. // StringReader 的方法,也是那几个:read() close() ready() reset() skip()
    4. // 使用默认的初始字符串缓冲区大小创建一个新的字符串写入器。
    5. public StringWriter(){}
    6. // 使用指定的初始字符串缓冲区大小创建一个新的字符串写入器。
    7. public StringWriter(int initialSize){}
    8. // StringWriter 的方法,同样也是:wirte() flush() close() append() toString()

    代码示例

    1. public static void main(String[] args) {
    2. try {
    3. StringReader sr = new StringReader("Hello World");
    4. StringWriter sw = new StringWriter();
    5. int read = sr.read();
    6. while (read != -1){
    7. System.out.print((char) read);
    8. sw.write(read);
    9. read = sr.read();
    10. }
    11. System.out.println();
    12. sw.append(" !!!");
    13. System.out.println(sw.toString());
    14. } catch (IOException e) {
    15. e.printStackTrace();
    16. }
    17. }

    总结

    在这些字符输入输出流里面,一样也是成对的出现去使用。解决一些问题,也是可以互相转换;

    用的多的也就是 BufferReader&BufferWirter、InputStreamReader&OutputStreamWriter这两对来操作文件的。

    PipedReader&PipedWriter、CharArrayReader&CharArrayWriter看场景情况。

    IO流这些吧,就是用来操作一些文件或者网络编程里面一些流的交互;其他基本用不到这些


  • 相关阅读:
    【C语言】初识指针(终篇)
    kubesphere安装Maven+JDK17 流水线打包
    jenkins拉取git代码 code 128解决方案
    学习笔记—tomcat调优
    #IIC 通信协议
    vant组件安装之后导入所有组件报错
    IX模式和其他模式
    DRF学习之三大认证
    内功心法:深入研究整型数(下)
    30+程序员辞职3年上岸985,妻子赚钱供父子俩读书,网友却泼冷水
  • 原文地址:https://blog.csdn.net/weixin_46129103/article/details/126092123