各位:请自带饮料自带茶,自带板凳自带糖,坐好,憋说话,我讲个故事,你仔细听,讲完留作业!
很久以前,有一群美国人,他们发明了计算机之后,想用一个字节也就是八位二进制数来表示一切可以形象化的文字。比如我想保存字母a
,我就把01100001
传给计算机帮我保存起来,这个过程叫编码(encode);反之,当计算机读取数据遇到01100001
时,计算机就显示字母a
,这个过程呢,叫做解码(decode)。后来这群人无耻的制定了一套标准,试图统治世界,这就是ASCII码。他们掰着手指加上能想到的控制符号,也就编到了127号,这群美国佬发现自己真是太牛逼了,他们天真的认为我们把128 - 255的编码位置留出来,我能encode一个宇宙。
后来,欧洲人也用计算机了,欧洲人发现美国佬真的是仗义,还给他们留着一半的编码空间,于是贪婪的欧洲佬把128-255的编码位置全都占了,这也就是我们常说的不是国际标准的扩展ASCII码表的其中一种。
再后来,计算机传到了我们伟大的祖国,当时的计算机专家掐指一算,大约最起码我们要6000多个编码位置就够啦,勤劳聪慧的中国人民就可以用上可以显示汉字的电脑啦,后来看了ASCII码表之后有一位专家提出,哎呀,256个,不如还是我们一起去学英语呀。后来这名专家被打了一顿之后,去了教育局。
其他的专家一看,我擦,有些字符集127号之后都是啥鬼画符,于是,我们伟大的前辈毫不犹豫的抛弃了那群贪婪的人添加的字符集扩展部分,并大胆提出:127号之前的兼容ACSII码表,我们把大于127的两个字节连在一起,表示一个汉字,第一个字节为高位字节
(也叫区字节
),第二个字节为低位字节
(也叫位字节
),高位字节
使用了0xA1-0xF7
,低位字节
使用了0xA1-0xFE
。这样我们大约搞出了6763个汉字以及非汉字图形符号682个,并且我们把ACSII本身存在的标点·数字·字母
重新以两个字节的编码方式重新搞了一个放到里面,这就是我们常说的全角字符
,原ACSII127号以下的也就是我们说的半角字符
啦。前辈们给它起了一个名字:信息交换用汉字编码字符集
,也就是我们常说的GB2312
。
好景不长,五千年的风和雨啊,不是闹着玩的,你爸去给你落户的时候发现计算机打不出这个字。面对这种情况,我们干脆不再要求位字节要大于127,只要区字节大于127就可以。它兼容GB2312,同时收录内容也提升至23940个码位,其中有汉字21003这也就是汉字内码扩展规范
,也就是我们常说的GBK
。
经过上面过程的演变,我国的人民终于可以更方便的使用计算机了。老一辈程序员也非常高兴,就这样,这种双字节字符集在坊间广泛流传了起来,相信一定听过一句名言:一个汉字是两个英文字符
。
就这样,在我们沾沾自喜的时候,世界各地计算机开发者以及使用者也如雨后春笋,字符集以及编码规范也是百家争鸣,互不兼容,乱码遍地,就在大家苦恼不已但又谁都不服谁的时候,有两拨人不仅意识到这个问题的严重性,而且着手创建一个通用字符集,其中一拨人就是国际标准化组织
(ISO
),他们制定了UCS
字符集(Universal Character Set
),另一拨人则是美国多语言服务提供商的财团发起的Unicode
项目,他们都用四位十六进制数,也就是两个字节来表示一个字符,也就是65535个码位(后续有UCS-4
,也就是四个字节表示一个字符,共21亿个字符,应该够用了哦)。后来,两拨人意识到世界上不需要两个统一的通用字符集,所以他们合作制定了一个字符表,也就是我们目前所见最多,打破你一个汉字是两个英文字符的观点的Unicode/UCS
(多少次老师殷切的叮咛变成你被其他程序员耻笑的理由)。
在这里要说一下,Unicode/UCS
只是字符集,每一个字符虽然对应一个码位,但是用什么字节以及多少个字节来表示这个字符,是由字符编码决定的,上面我们提到的ASCII
,GB2312
,GBK
等地区编码标准制定时,一般字符集和字符编码时同时制定的,也就是说ASCII
等不仅仅是字符集,更是一种字符编码格式。
Unicode只是一个字符集,它的字符编码有UTF-8,UTF-16,UTF-32。随着计算机网络的兴起,UTF-8编码越来越体现出他的优势:不仅可以向前兼容ASCII码,是变长的编码,并且由于编码没有状态,所以很容易重新同步,在传输过程中丢失了一些字节后,具有鲁棒性。
我们来看一下UTF-8对Unicode进行编码的方式如下:
比如,“汉”字的Unicode编码是U+6C49。0x6C49在0x0800-0xFFFF之间,使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
聪明的你肯定意识到我看到的Unicode码不是这样的啊,我看到的都是\u****
这样的,那就再提一嘴转译序列,在C语言中,\u6C49
代表字符名称为U+6C49
的Unicode码。
你肯定还去去试我例子中的UTF-8编码后的字节传能不能转化成汉字,于是你打开网页,搜索UTF-8转化,于是你快乐的把E6 B1 89
输入进去:得到的结果还是E6 B1 89
,你肯定有点懵逼,以为我在忽悠你,于是你打算取关我,于是你输入”汉“字,于是你得到的结果为汉
,先别着急取消关注我,听我解释啊:
其实,和上面我们提到的为什么\u****
并不是纯正的Unicode码位对应的十六进制数差不多,我们看到的东西,都是经过IDE或者Html等搞过的,所以它们会通过一些特殊的方法去简化或者说标志一种编码方式。
在XML及其子集HTML采用UTF-8作为标准字集,理论上我们可以在各种支持XML标准的浏览器上显示任何地区文字的网页,只要电脑本身安装有合适的字体即可。可以利用&#nnn;的格式显示特定的字符。nnn代表该字符的十进制 Unicode 代码。如果采用十六进制代码,在编码之前加上x字符即可。但部分旧版本的浏览器可能无法识别十六进制代码。是不是和你刚才的东西吻合了呢?
刚才取消关注的那个哥们儿,关注回来呗!
最后,我们来变个魔术:
在 windows 电脑上新建一个记事本,输入"联通"两个字之后,保存,关闭,然后再次打开,试一下,就是见证奇迹的时刻,不要懵逼,放开你的想象力,你知道为什么吗?