字符流
Reader
输入流:把数据(键盘输入、鼠标、扫描仪等等外设设备)读入到内存(程序)中
输出流:把内存(程序)中的数据输出到外设或其他地方,从文件角度简单总结就是,输入流就是读数据,输出流就是写数据。
Reader:输入流、继承自Reader的流都是用于向程序中输入数据的,且数据单位都是字符16位
Writer:输出流、继承自Writer的流都是程序用于向外输出数据的,且数据单位都是字符(16位)
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;
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(); // 关闭流
}
如果文件中包含中文,就会出现乱码,因为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 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())) {
}
Reader
定义了所有字符输入流的超类:
FileReader
实现了文件字符流输入,使用时需要指定编码;CharArrayReader
和StringReader
可以在内存中模拟一个字符流输入。
Reader
是基于InputStream
构造的:可以通过InputStreamReader
在指定编码的同时将任何InputStream
转换为Reader
。
总是使用try (resource)
保证Reader
正确关闭。
Writer
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
是所有字符输出流的超类,它提供的方法主要有:
- 写入一个字符(0~65535):
void write(int c)
; - 写入字符数组的所有字符:
void write(char[] c)
; - 写入String表示的所有字符:
void write(String s)
。
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:
}
Writer
定义了所有字符输出流的超类:
FileWriter
实现了文件字符流输出;CharArrayWriter
和StringWriter
在内存中模拟一个字符流输出。
使用try (resource)
保证Writer
正确关闭。
Writer
是基于OutputStream
构造的,可以通过OutputStreamWriter
将OutputStream
转换为Writer
,转换时需要指定编码。