天天看点

【java快速入门-IO篇】- Reader and WriterReaderWriter

以下内容介绍reader和writer的简单使用...后续遇到好的程序会不断更新

Reader

Reader

是Java的IO库提供的另一个输入流接口。和

InputStream

的区别是,

InputStream

是一个字节流,即以

byte

为单位读取,而

Reader

是一个字符流,即以

char

为单位读取:

【java快速入门-IO篇】- Reader and WriterReaderWriter

FileReader

FileReader

Reader

的一个子类,它可以打开文件并获取

Reader

。下面的代码演示了如何完整地读取一个

FileReader

的所有字符

public void readFile() throws IOException {
    // 创建一个FileReader对象:
    Reader reader = new FileReader("src/readme.txt"); //
    for (;;) {
        int n = reader.read(); // 反复调用read()方法,直到返回-1
        if (n == -1) {
            break;
        }
        System.out.println((char)n); // 打印char
    }
    reader.close(); // 关闭流
}
           

如果我们读取一个纯ASCII编码的文本文件,上述代码工作是没有问题的。但如果文件中包含中文,就会出现乱码,因为

FileReader

默认的编码与系统相关,例如,Windows系统的默认编码可能是

GBK

,打开一个

UTF-8

编码的文本文件就会出现乱码。

要避免乱码问题,我们需要在创建

FileReader

时指定编码:

Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8);
           

InputStream

类似,

Reader

也是一种资源,需要保证出错的时候也能正确关闭,所以我们需要用

try (resource)

来保证

Reader

在无论有没有IO错误的时候都能够正确地关闭:

try (Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8) {
    // TODO
}
           

一次性读取若干字符

public int read(char[] c) throws IOException
           

 它返回实际读入的字符个数,最大不超过

char[]

数组的长度。返回

-1

表示流结束。

利用这个方法,我们可以先设置一个缓冲区,然后,每次尽可能地填充缓冲区:

public void readFile() throws IOException {
    try (Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8)) {
        char[] buffer = new char[1000];
        int n;
        while ((n = reader.read(buffer)) != -1) {
            System.out.println("read " + n + " chars.");
        }
    }
}
           

CharArrayReader

CharArrayReader

可以在内存中模拟一个

Reader

,它的作用实际上是把一个

char[]

数组变成一个

Reader

,这和

ByteArrayInputStream

非常类似:

try (Reader reader = new CharArrayReader("Hello".toCharArray())) {
}
           

StringReader

StringReader

可以直接把

String

作为数据源,它和

CharArrayReader

几乎一样:

try (Reader reader = new StringReader("Hello")) {
}
           

 InputStreamReader(实现从字节流到字符流的转换)

Reader

本质上是一个基于

InputStream

byte

char

的转换器,那么,如果我们已经有一个

InputStream

,想把它转换为

Reader

,是完全可行的。

InputStreamReader

就是这样一个转换器,它可以把任何

InputStream

转换为

Reader

。示例代码如下:

// 持有InputStream:
InputStream input = new FileInputStream("src/readme.txt");
// 变换为Reader:
Reader reader = new InputStreamReader(input, "UTF-8");
           

构造

InputStreamReader

时,我们需要传入

InputStream

,还需要指定编码,就可以得到一个

Reader

对象。上述代码可以通过

try (resource)

更简洁地改写如下:

try (Reader reader = new InputStreamReader(new FileInputStream("src/readme.txt"), "UTF-8")) {
    // TODO:
}
           

上述代码实际上就是

FileReader

的一种实现方式。

使用

try (resource)

结构时,当我们关闭

Reader

时,它会在内部自动调用

InputStream

close()

方法,所以,只需要关闭最外层的

Reader

对象即可。

Writer

Writer

OutputStream

的区别如下:

【java快速入门-IO篇】- Reader and WriterReaderWriter

FileWriter

FileWriter

就是向文件中写入字符流的

Writer

。它的使用方法和

FileReader

类似:

try (Writer writer = new FileWriter("readme.txt", StandardCharsets.UTF_8)) {
    writer.write('H'); // 写入单个字符
    writer.write("Hello".toCharArray()); // 写入char[]
    writer.write("Hello"); // 写入String
}
           

 CharArrayWriter

CharArrayWriter

可以在内存中创建一个

Writer

,它的作用实际上是构造一个缓冲区,可以写入

char

,最后得到写入的

char[]

数组,这和

ByteArrayOutputStream

非常类似

try (CharArrayWriter writer = new CharArrayWriter()) {
    writer.write(65);
    writer.write(66);
    writer.write(67);
    char[] data = writer.toCharArray(); // { 'A', 'B', 'C' }
}
           

StringWriter

StringWriter

也是一个基于内存的

Writer

,它和

CharArrayWriter

类似。实际上,

StringWriter

在内部维护了一个

StringBuffer

,并对外提供了

Writer

接口。

OutputStreamWriter

除了

CharArrayWriter

StringWriter

外,普通的Writer实际上是基于

OutputStream

构造的,它接收

char

,然后在内部自动转换成一个或多个

byte

,并写入

OutputStream

。因此,

OutputStreamWriter

就是一个将任意的

OutputStream

转换为

Writer

的转换器:

try (Writer writer = new OutputStreamWriter(new FileOutputStream("readme.txt"), "UTF-8")) {
    // TODO:
}
           

上述代码实际上就是

FileWriter

的一种实现方式。