【Java I/O 流】4 - 3 字符集详解
§4-3 字符集详解
4-3.1 ASCII 与 GBK 字符集
在计算机中,数据都是以一串二进制的数字表示的。每一位二进制数字表示一个比特位(bit),每 8 个比特位组成一个字节(byte),构成计算机中最小的存储单元。每一个字节最多可以存储 \(2^8\) 种不同的数据。计算机在存储文字时,需要先将文字转换为一串串二进制数字,这一个过程就需要使用不同的字符集。
ASCII 编码字符集:ASCII,全称为美国信息交换标准码(American Standard Code for Information Interchange),是基于拉丁字母的一套计算机编码系统。主要用于显示现代英语,其扩展版本则可以部分支持其他西欧语言。
标准 ASCII 字符代码范围为 0 ~ 127,共计 128 个字符代码(控制字符 32 个,可打印字符 95 个),使用一个字节表示一个字符。
存储时,先查询字符对应的 ASCII 编码,转换为二进制(不足 8 位则高位补 0)后存储。读取时,逐字节读取,将二进制编码直接转换为十进制的 ASCII 编码,解码为对应的字符。
GB-2312 字符集:单字节存储字符对于汉字远远不够。1981 年,国家指定了一套用于汉字编码的国家标准《信息交换用汉字编码字符集 基本集》,编号为 GB-2312,收录了 7445 个图形字符,其中包括了 6763 个简体汉字(不含繁体)。
BIG-5字符集:由中国台湾实行的繁体中文标准字符集,共收录 13053 个中文字,于 1984 年实施。
GBK 字符集:基于 GB-2312 的扩展字符集,于 2003 年 3 月 17 日发布,收录了 21003 个汉字。包含 GB-13000-1 中的全部中日韩汉字,和 BIG-5 编码中的所有汉字。简体中文的 Windows 默认使用该字符集。
GBK 兼容了 ASCII 编码,意味着 ASCII 字符使用一个字节存储,存储规则同 ASCII 字符存储规则,汉字则使用两个字节存储。
存储汉字时,查询汉字所对应的 GBK 字符编码,转换为二进制(由两个字节的二进制数字表示,分别为高位和低位,高位的首位一定以 1 开头,转换为十进制后一定为负数)后存储。
读取时,根据字节的首位判断中西文字符,若为中文字符,将二进制编码直接转换为十进制,解码为对应的汉字。
ANSI:严格来说,ANSI 并不是一个特定的字符集,而是多套字符集的合集,根据 Windows 不同的地区和语言设置表示不同的编码。在中国大陆地区为 GBK,在美国为 ASCII,在韩国为 EUC-KR,在日本则为 Shift-JP。
4-3.2 Unicode 字符集
Unicode 字符集:国际标准字符集,它将世界各种语言的每个字符定义一个唯一的编码,以满足阔语言、阔平台的文本信息转换。
Unicode 提出了多种不同的字符编码方案,有 UTF-16(每一个字符统一用 16 个比特位,即双字节表示),UTF-32(每一个字符统一用 32 个比特位,即四字节表示),和当今最为常用的 UTF-8(可变长度编码,根据不同语言字符选择)。
UTF-8 是这样选择编码长度的:
- 单字节
0xxxxxxx
:ASCII 编码字符; - 双字节
110xxxxx 10xxxxxx
:拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文; - 三字节
1110xxx 10xxxxxx 10xxxxxx 10xxxxxx
:中日韩文字、东南亚文字、中东文字。
在存储时,先查询字符的 Unicode 编码,使用 UTF-8 编码,则会根据文字的不同选择不同的编码长度存储。
乱码:产生乱码的关键原因在于编码解码时采用的字符集不统一所致。不统一的字符集可能会导致读取时未能正确完整地读取一个字符的编码,编码所对应的字符不一致而导致的乱码。
使用字节流读取文件时,由于是逐个字节读取,往往会产生乱码的问题。为避免这种现象,不要使用字节流读取文本文件,编解码应当使用统一的编码方式和统一的码表。
产生乱码仅发生在编解码的过程中,若操作不涉及编解码,则不会发生乱码问题。
4-3.3 Java 中的不同字符集编解码实现
Java 中不同字符集的编解码实现依赖于 String
类的成员方法和构造方法。
编码:从字符串到字节
方法 | 描述 |
---|---|
bytes[] getBytes() |
使用平台默认的字符集将字符串转换为字节数组 |
bytes[] getBytes(String charset) |
使用指定的字符集将字符串转换为字节数组 |
注意:不支持的字符集会抛出异常 UnsupportedEncodingException
。
解码:从字节到字符串
方法 | 描述 |
---|---|
String(byte[] bytes) |
使用平台默认的字符集解码字节数组 |
String(byte[] bytes, String charset) |
使用指定字符集解码字节数组 |
注意:不支持的字符集会抛出异常 UnsupportedEncodingException
。
UnsupportedEncodingException
是一个编译时异常,继承自 IOException
。
示例:IDEA 的默认编解码方式为 UTF-8
//测试不同字符集的编解码
//原始数据
String str = "你好Java";
//默认方式编解码
byte[] defaultSet = str.getBytes();
String decodedDefault = new String(defaultSet);
System.out.println("平台默认方式编解码:" + decodedDefault);
//GBK编码规则编解码
byte[] gbks = str.getBytes("GBK"); //方法需要处理异常,此处写在方法签名处
String decodedGBK = new String(gbks, "GBK");
System.out.println("GBK方式编解码:" + decodedGBK);
//乱码原因:编解码方式不一致
String mess = new String(defaultSet, "GBK");
System.out.println("用GBK解码UTF-8:" + mess);
运行结果:
平台默认方式编解码:你好Java
GBK方式编解码:你好Java
用GBK解码UTF-8:浣犲ソJava