在这个示例中,首先定义了一个字符串变量 str 和一个字符集名称 charsetName。然后,使用
Charset.forName()
方法获取指定字符集的 Charset 对象。接着,使用字符串的 getBytes() 方法将字符串编码为指定字符集的字节数组。最后,使用
new String()
方法将字节数组解码为字符串。
需要注意的是,在编码和解码过程中,要保证使用相同的字符集,以便正确地转换数据。
02、字符集
Charset:字符集,是一组字符的集合,每个字符都有一个唯一的编码值,也称为码点。
常见的字符集包括 ASCII、Unicode 和 GBK,而 Unicode 字符集包含了多种编码方式,比如说 UTF-8、UTF-16。
ASCII 字符集
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)字符集是一种最早的字符集,包含 128 个字符,其中包括控制字符、数字、英文字母以及一些标点符号。ASCII 字符集中的每个字符都有一个唯一的 7 位二进制编码(由 0 和 1 组成),可以表示为十进制数或十六进制数。
ASCII 编码方式是一种固定长度的编码方式,每个字符都使用 7 位二进制编码来表示。ASCII 编码只能表示英文字母、数字和少量的符号,不能表示其他语言的文字和符号,因此在全球范围内的应用受到了很大的限制。
Unicode 字符集
Unicode 包含了世界上几乎所有的字符,用于表示人类语言、符号和表情等各种信息。Unicode 字符集中的每个字符都有一个唯一的码点(code point),用于表示该字符在字符集中的位置,可以用十六进制数表示。
为了在计算机中存储和传输 Unicode 字符集中的字符,需要使用一种编码方式。UTF-8、UTF-16 和 UTF-32 都是 Unicode 字符集的编码方式,用于将 Unicode 字符集中的字符转换成字节序列,以便于存储和传输。它们的差别在于使用的字节长度不同。
-
UTF-8 是一种可变长度的编码方式,对于 ASCII 字符(码点范围为
0x00~0x7F
),使用一个字节表示,对于其他 Unicode 字符,使用两个、三个或四个字节表示。UTF-8 编码方式被广泛应用于互联网和计算机领域,因为它可以有效地压缩数据,适用于网络传输和存储。
-
UTF-16 是一种固定长度的编码方式,对于基本多语言平面(Basic Multilingual Plane,Unicode 字符集中的一个码位范围,包含了世界上大部分常用的字符,总共包含了超过 65,000 个码位)中的字符(码点范围为
0x0000~0xFFFF
),使用两个字节表示,对于其他 Unicode 字符,使用四个字节表示。
-
UTF-32 是一种固定长度的编码方式,对于所有 Unicode 字符,使用四个字节表示。
GBK 字符集
GBK 包含了 GB2312 字符集中的字符,同时还扩展了许多其他汉字字符和符号,共收录了 21,913 个字符。GBK 采用双字节编码方式,每个汉字占用 2 个字节,其中高字节和低字节都使用了 8 位,因此 GBK 编码共有
2^16=65536
种可能的编码,其中大部分被用于表示汉字字符。
GBK 编码是一种变长的编码方式,对于 ASCII 字符(码位范围为 0x00 到 0x7F),使用一个字节表示,对于其他字符,使用两个字节表示。GBK 编码中的每个字节都可以采用 0x81 到 0xFE 之间的任意一个值,因此可以表示
2^15=32768
个字符。为了避免与 ASCII 码冲突,GBK 编码的第一个字节采用了 0x81 到 0xFE 之间除了 0x7F 的所有值,第二个字节采用了 0x40 到 0x7E 和 0x80 到 0xFE 之间的所有值,共 94 个值。
GB2312 的全名是《信息交换用汉字编码字符集基本集》,也被称为“国标码”。采用了双字节编码方式,每个汉字占用 2 个字节,其中高字节和低字节都使用了 8 位,因此 GB2312 编码共有
2^16=65536
种可能的编码,其中大部分被用于表示汉字字符。GB2312 编码中的每个字节都可以采用 0xA1 到 0xF7 之间的任意一个值,因此可以表示 126 个字符。
GB2312 是一个较为简单的字符集,只包含了常用的汉字和符号,因此对于一些较为罕见的汉字和生僻字,GB2312 不能满足需求,现在已经逐渐被 GBK、GB18030 等字符集所取代。
GB18030 是最新的中文码表。收录汉字 70244 个,采用多字节编码,每个字可以由 1 个、2 个或 4 个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
03、乱码
当使用不同的编码方式读取或者写入文件时,就会出现乱码问题,来看示例。
String s = "沉默王二!";
try {
OutputStreamWriter out = new OutputStreamWriter(
new FileOutputStream("logs/test_utf8.txt"), "GBK");
out.write(s);
out.close();
FileReader fileReader = new FileReader("logs/test_utf8.txt");
int read;
while ((read = fileReader.read()) != -1) {
System.out.print((char)read);
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
在上面的示例代码中,首先定义了一个包含中文字符的字符串,然后将该字符串按 GBK 编码方式保存到文件中,接着将文件按默认编码方式(UTF-8)读取,并显示内容。此时就会出现乱码问题,显示为“��Ĭ������”。
这是因为文件中的 GBK 编码的字符在使用 UTF-8 编码方式解析时无法正确解析,从而导致出现乱码问题。
那如何才能解决乱码问题呢?
这就引出我们今天的主角了——转换流。
java.io.InputStreamReader
是 Reader 类的子类。它的作用是将字节流(InputStream)转换为字符流(Reader),同时支持指定的字符集编码方式,从而实现字符流与字节流之间的转换。
1)构造方法
-
InputStreamReader(InputStream in)
: 创建一个使用默认字符集的字符流。
-
InputStreamReader(InputStream in, String charsetName)
: 创建一个指定字符集的字符流。
代码示例如下:
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
2)解决编码问题
下面是一个使用 InputStreamReader 解决乱码问题的示例代码:
String s = "沉默王二!";
try {
OutputStreamWriter outUtf8 = new OutputStreamWriter(
new FileOutputStream("logs/test_utf8.txt"), "GBK");
outUtf8.write(s);
outUtf8.close();
InputStreamReader isr = new InputStreamReader(new FileInputStream("logs/test_utf8.txt"), "GBK");
int c;
while ((c = isr.read()) != -1) {
System.out.print((char) c);
isr.close();
} catch (IOException e) {
e.printStackTrace();
由于使用了 InputStreamReader 对字节流进行了编码方式的转换,因此在读取字符流时就可以正确地解析出中文字符,避免了乱码问题。
05、OutputStreamWriter
java.io.OutputStreamWriter
是 Writer 的子类,字面看容易误以为是转为字符流,其实是将字符流转换为字节流,是字符流到字节流的桥梁。
-
OutputStreamWriter(OutputStream in)
: 创建一个使用默认字符集的字符流。
-
OutputStreamWriter(OutputStream in, String charsetName)
:创建一个指定字符集的字符流。
代码示例如下:
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("a.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("b.txt") , "GBK");
通常为了提高读写效率,我们会在转换流上再加一层
缓冲流
open in new window
,来看代码示例:
try {
FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
BufferedReader br = new BufferedReader(isr);
FileOutputStream fos = new FileOutputStream("output.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
br.close();
bw.close();
} catch (IOException e) {