在 Java 的 I/O 体系中,InputStreamReader 类扮演着极为关键的角色。它通过对 InputStream 进行封装,实现了从字节流到字符流的转换,成功将原本基于字节的输入流转化为能够处理字符数据的 Reader。
InputStreamReader 能够将输入流中的字节数据按照特定的字符编码规则解析为文本内容,而不再将其视为单纯的数字数据,这一特性使其成为处理文本输入的重要工具。从类继承体系来看,InputStreamReader 类继承自 Reader 类,这也体现了它作为字符流处理组件的本质属性。
在实际应用场景中,InputStreamReader 常被用于从文件系统或网络连接中读取字符数据,特别是当字节数据代表文本信息时,其作用尤为显著。以 UTF-8 编码的文本文件为例,开发者可以通过InputStreamReader 类对 FileInputStream 进行封装,从而实现对该文件的字符读取操作,确保文本内容能够被正确解析和处理。
以下是 InputStreamReader 的示例:
package com.hxstrive.java_io; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.Reader; public class InputStreamReaderExample { public static void main(String[] args) throws Exception { try(Reader inputStreamReader = new InputStreamReader(new FileInputStream("input.txt"))) { int data = inputStreamReader.read(); while (data != -1) { char theChar = (char) data; System.out.print(theChar); data = inputStreamReader.read(); } System.out.println(); } } } //结果: //你好!Java世界
上述示例,首先创建了一个 FileInputStream,然后将其封装在一个 InputStreamReader 中。其次,通过 InputStreamReader 读取文件中的所有字符。
要创建一个 InputStreamReader,你只需像实例化任何其他对象一样实例化它,将一个 InputStream 传递给它的构造函数。如下:
InputStream inputStream = new FileInputStream("input.txt"); // 创建一个 InputStreamReader Reader inputStreamReader = new InputStreamReader(inputStream);
底层 InputStream 里的字符会采用特定的字符编码(如 UTF-8、GBK 等等)写入,如果要读取字符,则需要使用和写入一致的编码,否则就会出现乱码。注意,这里的编码在 Java 中被称作字符集(Charset)。
字符集是一套字符的集合,以及将这些字符与二进制数据相互转换的规则。它规定了如何对字符进行编码和解码,以便计算机能够存储、处理和传输文本数据。
常见的字符集有:
ASCII 字符集:是最早的字符集之一,它使用 7 位或 8 位二进制数来表示 128 或 256 个不同的字符,包括英文字母、数字、标点符号等基本字符。
Unicode 字符集:为了统一全球各种语言的字符表示而设计,它包含了几乎世界上所有的字符,为每个字符分配了一个唯一的编号,称为码点。Unicode 有多种编码方式,如 UTF-8、UTF-16 和 UTF-32 等。
UTF-8:是 Unicode 的一种可变长度编码方式,它可以用 1 到 4 个字节来表示一个字符。UTF-8 具有良好的兼容性和空间效率,是目前互联网上最常用的字符编码之一。
GB2312:是中国国家标准的简体中文字符集,收录了 6763 个常用汉字和 682 个非汉字字符,采用双字节编码。
GBK:是在 GB2312 基础上扩展的字符集,收录了更多的汉字和符号,支持繁体中文和一些少数民族文字,兼容 GB2312。
在使用 InputStreamReader 时,你需要告诉 InputStreamReader 的实例,底层 InputStream 中的字符是用何种字符集编码的,这样它才能正确读取和解码。要设置编码,可以在 InputStreamReader 的构造函数中指定编码。例如:
package com.hxstrive.java_io; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; public class InputStreamReader2Example { public static void main(String[] args) throws Exception { try(Reader inputStreamReader = new InputStreamReader(new FileInputStream("input.txt"), StandardCharsets.UTF_8)) { int data = inputStreamReader.read(); while (data != -1) { char theChar = (char) data; System.out.print(theChar); data = inputStreamReader.read(); } System.out.println(); } } } //结果: //你好!Java世界
如上示例,字符集作为第二个参数传递给 InputStreamReader 构造函数,这里使用的字符是 UTF-8。
要获取字符编码,可以通过 InputStreamReader 实例的 getEncoding() 方法获取该实例使用的字符编码。例如:
String encoding = inputStreamReader.getEncoding();
InputStreamReader 的 read() 方法返回一个 int,其中包含读取的字符的字符值。例如:
int data = inputStreamReader.read();
你可以像这样,将返回的 int 类型强制转换为 char 类型:
char aChar = (char) data;
如果 read() 方法返回 -1,则表示已到达流的末尾,即 InputStreamReader 中已无数据可读。此时,可以将 InputStreamReader 关闭。
通常,我们会在一个 while 循环中从 InputStreamReader 读取数据,就像下面这样:
int data = inputStreamReader.read(); // 读取数据 while(data != -1) { char theChar = (char) data; data = inputStreamReader.read(); // 读取数据 } inputStreamReader.close(); // 关闭
正如你所看到的,while 循环将一直运行,直到从 InputStreamReader 的 read() 方法中读取到数字 -1。之后,InputStreamReader 将被关闭。
当我们完成从 InputStreamReader 读取字符后,应记得关闭它。关闭 InputStreamReader 也将关闭底层的 InputStream 实例。
如果要关闭 InputStreamReader,仅需要调用其 close() 方法。如下:
inputStreamReader.close();
甚至,你还可以使用 Java7 中引入的 try-with-resources 结构,自动关闭资源。例如
try(Reader inputStreamReader = new InputStreamReader(new FileInputStream("input.txt"), StandardCharsets.UTF_8)) { int data = inputStreamReader.read(); while (data != -1) { char theChar = (char) data; System.out.print(theChar); data = inputStreamReader.read(); } System.out.println(); }
注意,我们在这里没有明确调用 close() 方法,try-with-resources 结构会在结束的时候自动帮我们调用 close() 方法,关闭资源。