Reader
是Java的IO库提供的另一个输入流接口。和 InputStream
的区别是,InputStream
是一个字节流,即以 byte
为单位读取,而 Reader
是一个 字符 流,即以 char
为单位读取:
InputStream | Reader |
---|---|
字节流,以byte为单位 | 字符流,以char为单位 |
读取字节(-1,0~255):int read() | 读取字符(-1,0~65535):int read() |
读到字节数组:int read(byte[] b) | 读到字符数组:int read(char[] c) |
java.io.Reader
是所有字符输入流的超类,它最主要的方法是:
public int read() throws IOException;
这个方法读取字符流的下一个字符,并返回字符表示的 int
,范围是 0~65535。如果已读到末尾,返回 -1。
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
}
Reader
还提供了一次性读取若干字符并填充到 char[]数组
的方法:
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.");
}
}
}
StringReader
可以直接把 String
作为数据源:
try (Reader reader = new StringReader("Hello")) {
}
Reader
是带编码转换器的 InputStream
,它把 byte
转换为 char
,而 Writer
就是带编码转换器的 OutputStream
,它把 char
转换为 byte
并输出。
Writer
和 OutputStream
的区别如下:
OutputStream | Writer |
---|---|
字节流,以byte为单位 | 字符流,以char为单位 |
写入字节(0~255):void write(int b) | 写入字符(0~65535):void write(int c) |
写入字节数组:void write(byte[] b) | 写入字符数组:void write(char[] c) |
无对应方法 | 写入String:void write(String s) |
Writer
是所有字符输出流的超类,它提供的方法主要有:
void write(int c)
;void write(char[] c)
;void write(String s)
。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
}
StringWriter
也是一个基于内存的 Writer
。实际上,StringWriter 在内部维护了一个 StringBuffer
,并对外提供了 Writer
接口。