什么是“锟斤拷”?我竟答不上来......
以下文章来源于漫话编程 ,作者漫话编程
周末女朋友出去逛街了,我自己一个人在家看综艺节目,突然,女朋友给我打来电话。
要想知道什么是乱码,需要先从计算机编码说起。
字符编码和 ASCII
我们经常看一些谍战剧,谍战剧里敌特、地下党员以及八路军各部间发送情报的时候,一般都是通过电报发送的。
电报在传递的过程中,需要发报员用电键发出长短不一的电码,收报员就会听到电报机发出的滴滴滴答答答的声音。
谍战剧中将情报转成电报的"滴"和"答"声主要通过摩尔斯电码,这是一种通过不同的排列顺序来表达不同的英文字母、数字和标点符号的字符编码方式。
就像电报只能发出"滴"和"答"声一样,计算机只认识 0 和 1 两种字符,但是,人类的文字是多种多样的,如何把人类的文字转换成计算机认识的 01 字符呢,这个过程同样需要通过字符编码。
字符编码(Character encoding)是一套法则,使用该法则能够对自然语言的字符的一个集合(如字母表或音节表),与其他东西的一个集合(如号码或电脉冲)进行配对。
和摩尔斯电码功能类似,上个世纪 60 年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定,这被称为 ASCII 码,一直沿用至今。
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套计算机编码系统。
在介绍其他的字符编码之前,我们先来说一下一个计算机领域通用的字符集。
Unicode
Unicode(中文:万国码、国际码、统一码、单一码)是计算机科学领域里的一项业界标准。
它对世界上大部分的文字系统进行了整理、编码,使得计算机可以用更为简单的方式来呈现和处理文字。
UTF-8,UTF-16,UTF-32
Unicode 虽然统一了全世界字符的编码,但没有规定如何存储。这么做是有考虑的:如果 Unicode 统一规定,每个符号就要用 3 个或 4 个字节表示,因为字符太多,只能用这么多字节才能表示完全。
一旦这么规定,那么每个英文字母前都必然有 2 到 3 个字节是 0,因为所有英文字母在 ASCII 中都有,都可以用 1 个字节表示,剩余字节位置就要补充 0。
如果这样,文本文件的大小会因此大出二三倍,这对于存储来说是极大的浪费。
为了解决这个问题,就出现了一些中间格式的字符集,他们被称为通用转换格式,即 UTF(Unicode Transformation Format)。
常见的 UTF 格式有:
UTF-7
UTF-7.5
UTF-8
UTF-16
UTF-32
UTF-8:使用 1 至 4 个字节为每个字符编码,UTF-16:使用 2 或 4 个字节为每个字符编码,UTF-32:使用 4 个字节为每个字符编码。
所以我们可以说,UTF-8、UTF-16 等都是 Unicode 的一种实现方式。
举个例子,Unicode 规定了 1 个中文字符 "我"对应的 Unicode 是 "\u6211",但是,在 UTF-8 和 UTF-16 等不同的实现方式下,这个二进制 Code 的存储方式是不一样的。
GBK,GB2312,GB18030
因为 UTF-8 是 Unicode 的一种实现,所以他包含了世界上的所有文字的编码,他采用的是 1-4 字节进行编码。
对于那些排在前面优先纳入的文字,可能就优先使用 1 字节、2 字节存储了,对于后纳入的文字,就要使用 3 字节或者 4 字节存储了。
正是因为他太全了,所以那些晚一些纳入的字符,在 UTF-8 中的存储所占的字节数可能就会多一些,那他的存储空间要求就会很大。
对于常用的汉字,在 UTF-8 中采用 3 字节进行编码,但是如果有一种只包含中文和 ASCII 的编码的话,就不需要使用 3 个字节,可能 2 个字节就够了。
对于大部分网站来说,基本都是只服务一个国家或者地区的,比如一个中国的网站,一般会出现简体字和繁体字以及一些英文字符,很少会出现日语或者韩文的。
也是出于这样的考虑,中国国家标准总局于 1981 年制定并实施了 GB 2312-80 编码,即中华人民共和国国家标准简体中文字符集。
后来厂商微软利用 GB 2312-80 未使用的编码空间,收录 GB 13000.1-93 全部字符制定了 GBK 编码。
有了标准中文字符集,如果是一个纯中文网站,就可以采用这种编码方式,这样可以大大节省一些存储空间的。
常用的中文编码有 GBK,GB2312,GB18030 等,最常用的是 GBK。
GB2312(1980 年),16 位字符集,收录有 6763 个简体汉字,682 个符号,共 7445 个字符:
优点:适用于简体中文环境,属于中国国家标准,通行于大陆,新加坡等地。
缺点:不兼容繁体中文,其汉字集合过少。
GBK(1995 年),16 位字符集,收录有 21003 个汉字,883 个符号,共 21886 个字符:
优点:适用于简繁中文共存的环境,为简体 Windows 所使用,向下完全兼容 GB2312,向上支持 ISO-10646 国际标准 ;所有字符都可以一对一映射到 Unicode 2.0 上。
缺点:不属于官方标准和 big5 之间需要转换;很多搜索引擎都不能很好地支持 GBK 汉字。
GB18030(2000 年),32 位字符集;收录了 27484 个汉字,同时收录了藏文、蒙文、维吾尔文等主要的少数民族文字:
优点:可以收录所有你能想到的文字和符号,属于中国最新的国家标准。
缺点:目前支持它的软件较少。
乱码
就像在计算机领域,我们把一串中文字符通过 UTF-8 进行编码传输给别人,别人拿到这串文字之后,通过 GBK 进行解码,得到的内容就会是“锟届瀿锟斤拷雮傡锟斤拷直锟斤拷锟”,这就是乱码。
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "漫话编程!";
byte[] bytes = s.getBytes(Charset.forName("GBK"));
System.out.println("GBK编码,GBK解码:" + new String(bytes, "GBK"));
System.out.println("GBK编码,GB18030解码:" + new String(bytes, "GB18030"));
System.out.println("GBK编码,UTF-8解码:" + new String(bytes, "UTF-8"));
}
输出结果:
GBK编码,GBK解码:漫话编程!
GBK编码,GB18030解码:漫话编程!
GBK编码,UTF-8解码:????????
可以看到,将中文字符,通过 GBK 编码,再使用 UTF-8 解码,得到的字符就是一串问号,这就是乱码了。
锟斤拷的前世今生
也就是"0xFFFD REPLACEMENT CHARACTER",所有无法表示的字符都会通过这个字符来表示。
Unicode 官方有关于这个符号的介绍,从上表中可以看到,他的 10 进制表示是 65533,在 UTF-8 下,他的 16 进制形式是'0xEF 0xBF 0xBD'(三个字节)。
如果有两个连续的字符都无法显示,如"� �" ,那么在 UTF-8 编码下,16 进制表示为:
0xEF 0xBF 0xBD
0xEF 0xBF 0xBD
以上这段编码,如果放到 GBK 中进行解码的话,因为 GBK 中一个汉字两个字节,那么结果就是:
0xEF 0xBF, 0xBD 0xEF, 0xBF 0xBD
即:
0xEFBF
0xBDEF
0xBFBD
VC 会把栈中新分配的内存初始化为 0xcc,而把堆中新分配的内存初始化为 0xcd。把 0xcc 和 0xcd 按照字符打印出来,就是烫和屯了。
作者:漫话编程
编辑:陶家龙、孙淑娟
出处:转载自微信公众号:漫话编程(ID:mhcoding)
精彩文章推荐: