在早期的计算机系统中,为了给字符编码,美国国家标准学会(American National Standard Institute:ANSI)制定了一套英文字母、数字和常用符号的编码,它占用一个字节,编码范围从0
到127
,最高位始终为0
,称为ASCII
编码。例如,字符A
的编码是0x41
,字符1
的编码是0x31
。
如果要把汉字也纳入计算机编码,很显然一个字节是不够的。GB2312
标准使用两个字节表示一个汉字,其中第一个字节的最高位始终为1
,以便和ASCII
编码区分开。例如,汉字中
的GB2312
编码是0xd6d0
。
类似的,日文有Shift_JIS
编码,韩文有EUC-KR
编码,这些编码因为标准不统一,同时使用,就会产生冲突。
为了统一全球所有语言的编码,全球统一码联盟发布了Unicode
编码,它把世界上主要语言都纳入同一个编码,这样,中文、日文、韩文和其他语言就不会冲突。
Unicode
编码需要两个或者更多字节表示,我们可以比较中英文字符在ASCII
、GB2312
和Unicode
的编码:
英文字符A
的ASCII
编码和Unicode
编码:
┌────┐
ASCII: │ 41 │
└────┘
┌────┬────┐
Unicode: │ 00 │ 41 │
└────┴────┘
英文字符的Unicode
编码就是简单地在前面添加一个00
字节。
中文字符中
的GB2312
编码和Unicode
编码:
┌────┬────┐
GB2312: │ d6 │ d0 │
└────┴────┘
┌────┬────┐
Unicode: │ 4e │ 2d │
└────┴────┘
那我们经常使用的UTF-8
又是什么编码呢?因为英文字符的Unicode
编码高字节总是00
,包含大量英文的文本会浪费空间,所以,出现了UTF-8
编码,它是一种变长编码,用来把固定长度的Unicode
编码变成1~4
字节的变长编码。通过UTF-8
编码,英文字符A
的UTF-8
编码变为0x41
,正好和ASCII
码一致,而中文中
的UTF-8
编码为3
字节0xe4b8ad
。
UTF-8
编码的另一个好处是容错能力强。如果传输过程中某些字符出错,不会影响后续字符,因为UTF-8
编码依靠高字节位来确定一个字符究竟是几个字节,它经常用来作为传输编码。
在Java中,char
类型实际上就是两个字节的Unicode
编码。如果我们要手动把字符串转换成其他编码,可以这样做:
byte[] b1 = "Hello".getBytes(); // 按系统默认编码转换,不推荐
byte[] b2 = "Hello".getBytes("UTF-8"); // 按UTF-8编码转换
byte[] b2 = "Hello".getBytes("GBK"); // 按GBK编码转换
byte[] b3 = "Hello".getBytes(StandardCharsets.UTF_8); // 按UTF-8编码转换
注意:转换编码后,就不再是char
类型,而是byte
类型表示的数组。
如果要把已知编码的byte[]
转换为String
,可以这样做:
byte[] b = ...
String s1 = new String(b, "GBK"); // 按GBK转换
String s2 = new String(b, StandardCharsets.UTF_8); // 按UTF-8转换
始终牢记:Java的String
和char
在内存中总是以Unicode
编码表示。
©查看原文