趣谈字符集

来源:互联网 发布:语音通话交友软件 编辑:程序博客网 时间:2024/05/19 03:19

        很久很久以前,有一群人,他们决定用8个可以开闭的晶体管来组合成不同的状态,以表示世界上的万物。他们把这8位称为"字节"。 再后来,他们又做了一些可以处理这些字节的机器,机器开动了。用字节来组合出很多状态存在于这些机器上,这此机器就被称为"计算机"。 

        开始计算机只在美国用,8位的字节一共可以组合出256(2的8次方)种不同的状态。 他们把其中的编号从0~31这32种状态分别规定了特殊的用途,一旦打印机等终端遇上约定好的这些字节被传过来时,就要做一些约定的动作。遇上0x10终端就换行,遇上0x07终端就嘟嘟叫,遇上0x1b打印机就打印反白的字,或者终端就用彩色显示字母。他们把这32个0x20以下的字节状态称为"控制码"。 他们又把所有的空格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就可以用不同字节来存储英语的文字了。于是大家就把这个方案叫做"ASCII编码"(American Standard Code for Information Interchange,美国信息交换标准代码)。当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。 

        后来,欧洲各地的都开始使用计算机,但是很多国家用的不是英文,他们的字母里有许多是ASCII里没有的。为了支持多种地区的语言,各大组织机构或IT厂商开始发明它们自己的编码方案,以便弥补ASCII编码的不足。一时间,各种互不相容的字符编码方案成百花齐放之势。为了避免混乱,ISO组织(国际标准化组织International Organization for Standardization,总部在瑞士)在1998年之后,陆续发表了一系列代号为8859的标准,作为ASCII编码的标准扩展,终于统一了单字节的西方字符的编码。方式采用127号之后的空位来表示这些新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128到255的字符集被称"扩展字符集"。

        ISO-8859标准是单字节编码标准,俗称“欧洲码”,包括: 

        ISO-8859-1(Latin1 - 西欧字符):覆盖了大多数西欧语言,包括:法国、西班牙、葡萄牙、意大利、荷兰、德国、丹麦、瑞典、挪威、芬兰、冰岛、爱尔兰、苏格兰、英格兰等,因而也涉及到了整个美洲大陆、澳大利亚和非洲很多国家的语言。后来被采纳为ISO-10646标准,换句话说,Unicode的最开头256个字符编码和ISO-8859-1是一一对应的。

        ISO-8859-2(Latin2 - 中、东欧字符) 

        ISO-8859-3(Latin3 - 南欧字符) 

        ISO-8859-4(Latin4 - 北欧字符) 

        ISO-8859-5(Cyrillic - 斯拉夫语) 

        ISO-8859-6(Arabic - 阿拉伯语) 

        ISO-8859-7(Greek - 希腊语) 

        ISO-8859-8(Hebrew - 希伯来语) 

        ISO-8859-9(Latin5) 

        ISO-8859-10(Latin6) 

        ISO-8859-11(Thai - 泰国语) 

        ISO-8859-12(保留) 

        ISO-8859-13(Latin7) 

        ISO-8859-14(Latin8) 

        ISO-8859-15(Latin9)

        遗憾的是ISO-8859系列标准的字符编码是互不相容,不可能同时使用的。毕竟它们只是单字节的编码方案,而且它们和多字节的编码方案如中文编码GB2312和BIG5也是不相容的。那些欧洲字符中(最高位为1的字符),在GB2312和BIG5中被认为是双字节汉字编码的首字节。

        自此,贪婪的人类再没有新的状态可以用了,美帝可能没有想到第三世界国家的人们也希望可以用到计算机吧! 

        等到中国人、日本人和韩国人使用计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存呢。于是ISO组织又制定了ISO-2022标准(Character code structure and extension techniques),提供了七位与八位编码字符集的扩充方法的标准。我国根据ISO-2022制定了国家标准GB2312,其他东方国家和地区也制定了各自的字符编码标准,如日本的JIS0208,韩国的KSC5601,台湾地区的CNS11643等。

        BIG5是从CNS11643的早期版本发展而来的,虽然没有包括CNS11643的全部内容,但却是目前台湾、香港地区普遍使用的一种繁体汉字的编码标准,包括440个符号,一级汉字5401个、二级汉字7652个,共计13060个汉字。

        而聪明的中国人毫不客气地把那些127号之后的奇异符号们直接取消掉并规定:小于128的字符意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名都编进去了,连 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。 于是就把这种汉字方案叫做 "GB2312"。GB2312 是对 ASCII 的简体中文扩展,共收录6763个简体汉字、682个符号,其中汉字部分:一级字3755,以拼音排序,二级字3008,以偏旁排序。该标准的制定和应用为规范、推动中文信息化进程起了很大作用。 。 

        但是中国的汉字太多了,我们很快就发现有许多人的人名没有办法在这里打出来,特别是某些很会麻烦别人的国家领导人。于是我们不得不继续把 GB2312 没有用到的码位找出来用上,后来还是不够用。于是干脆不再要求低字节一定是127号之后的字节,只要高字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK 包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号,包含21003个汉字,也包含了ISO 10646中的全部中日韩文字、汉字简、繁体字。严格说,GBK不能算是国家标准,最多算是一个商业标准。

        后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK扩成了GB18030。从此之后,中华民族的文化就可以通过计算机传承了,而GB18030才成为真正的国家标准。 

        中国的程序员们看到这一系列汉字编码的标准不错,于是通称他们叫做 "DBCS"(Double Byte Charecter Set,双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里。因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。凡是受过加持,会编程的计算机僧侣们都要每天念下面这个咒语数百遍:"一个汉字算两个英文字符!一个汉字算两个英文字符……" 。

        因为当时各个国家都像中国这样搞出一套自己的编码标准,结果互相之间谁也不懂谁的编码,谁也不支持别人的编码,连大陆和台湾这样只相隔了150海里,使用着同一种语言的兄弟地区,也分别采用了不同的DBCS编码方案。当时的中国人想让电脑显示汉字,就必须装上一个"汉字系统",专门用来处理汉字的显示、输入的问题,但是那个台湾的愚昧封建人士写的算命程序就必须加装另一套支持BIG5编码的什么"倚天汉字系统"才可以用,装错了字符系统,显示就会乱了套!这怎么办?而且世界民族之林中还有那些一时用不上电脑的穷苦人民,他们的文字又怎么办?

        正在这时,大天使加百列又及时出现了。ISO组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞了一个包括了地球上所有文化、所有字母和符号的编码!他们打算叫它"Universal Multiple-Octet Coded Character Set",简称 UCS或ISO-10646标准。Unicode则是Unicode学术学会(The Unicode Consortium)制定的字符编码,Unicode编码与ISO 10646国际编码标准从内容上来说是同步一致的。虽然两个组织保持如此密切的合作关系,但Unicode和ISO 10646还是有区别的。ISO 10646着重定义字符编码,而Unicode则在此基础上,为这些字符及编码数据提出应用的方法以及对语义数据作补充。不管是Unicode编码还是ISO-10646编码,都约定俗成地称为Unicode编码。

        Unicode开始制订时,计算机的存储器容量极大地被提升了,空间再也不成为问题了。于是ISO就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ASCII里的那些“半角”字符,Unicode保持其原编码不变,只是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于"半角"英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。

        这时候,从旧社会走过来的程序员开始发现一个奇怪的现象:他们的strlen函数靠不住了,一个汉字不再是相当于两个字符了,而是一个!是的,从Unicode开始,无论是半角的英文字母,还是全角的汉字,它们都是统一的"一个字符"!同时也都是统一的"两个字节",请注意"字符"和"字节"两个术语的不同,“字节”是一个8位的物理存贮单元,而“字符”则是一个文化相关的符号。在Unicode中,一个字符就是两个字节。一个汉字算两个英文字符的时代已经快过去了。 

        以前多种字符集存在时,那些做多语言软件的公司遇上过很大麻烦,他们为了在不同的国家销售同一套软件,就不得不在区域化软件时也加持那个双字节字符集咒语,不仅要处处小心不要搞错,还要把软件中的文字在不同的字符集中转来转去。Unicode对于他们来说是一个很好的一揽子解决方案,于是从 Windows NT 开始,MS 趁机把它们的操作系统改了一遍,把所有的核心代码都改成了用Unicode方式工作的版本,从这时开始,WINDOWS系统终于无需要加装各种本土语言系统,就可以显示全世界上所有国家的字符了。 

        但是,Unicode在制订时没有考虑与任何一种现有的编码方案保持兼容,这使得GBK与Unicode在汉字上的编码完全不一样,没有一种简单的算术方法可以把文本内容从Unicode编码和另一种编码进行转换,这种转换必须通过查表来进行。 

        如前所述,Unicode是用两个字节来表示为一个字符,总共可以组合出65536个不同的字符,这大概已经可以覆盖世界上所有文化的符号。如果还不够也没有关系,ISO已经准备了UCS-4方案,说简单了就是四个字节来表示一个字符,这样我们就可以组合出21亿个不同的字符出来(最高位有其他用途),这大概可以用到银河联邦成立那一天吧! 

        Unicode来到时,一起到来的还有计算机网络的兴起,Unicode如何在网络上传输也是一个必须考虑的问题,于是面向传输的众多UTF(UCS Transfer Format)标准出现了,顾名思义,UTF8就是每次8个位传输数据,而UTF16就是每次16个位,只不过为了传输时的可靠性,从Unicode到UTF时并不是直接的对应,而是要过一些算法和规则来转换。 

        受到过网络编程加持的计算机僧侣们都知道,在网络里传递信息时有一个很重要的问题,就是对于数据高低位的解读方式,一些计算机是采用低位先发送的方法,例如我们PC机采用的INTEL架构。而另一些是采用高位先发送的方式,在网络中交换数据时,为了核对双方对于高低位的认识是否是一致的,采用了一种很简便的方法,就是在文本流的开始时向对方发送一个标志符——如果之后的文本是高位,那就发送"FEFF",反之,则发送"FFFE"。不信你可以用二进制方式打开一个UTF-X格式的文件,看看开头两个字节是不是这两个字节? 

        讲到这里,我们再顺便说说一个很著名的奇怪现象:当你在windows的记事本里新建一个文件,输入"联通"两个字之后,保存,关闭,然后再次打开,你会发现这两个字已经消失了,代之的是几个乱码!有人说这就是联通之所以拼不过移动的原因。 其实这是因为GB2312编码与UTF8编码产生了编码冲撞的原因。 

        从网上引来一段从Unicode到UTF8的转换规则: 

UnicodeUTF-80000 - 007F0xxxxxxx0080 - 07FF110xxxxx 10xxxxxx0800 - FFFF1110xxxx 10xxxxxx 10xxxxxx

        例如"汉"字的Unicode编码是6C49。6C49在0800-FFFF之间,所以要用3字节模板:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 1100 0100 1001,将这个比特流按三字节模板的分段方法分为0110 110001 001001,依次代替模板中的x,得到:1110-0110 10-110001 10-001001,即E6 B1 89,这就是其UTF8的编码。

        而当你新建一个文本文件时,记事本的编码默认是ANSI, 如果你在ANSI的编码输入汉字,那么实际就是GB系列的编码方式,在这种编码下,"联通"的编码是: 

c1 1100 0001 
aa 1010 1010 
cd 1100 1101 
a8 1010 1000 

        注意到了吗?第一二个字节、第三四个字节的起始部分的都是"110"和"10",正好与UTF8规则里的两字节模板是一致的,于是再次打开记事本时,记事本就误认为这是一个UTF8编码的文件,让我们把第一个字节的110和第二个字节的10去掉,我们就得到了"00001 101010",再把各位对齐,补上前导的0,就得到了"0000 0000 0110 1010",不好意思,这是Unicode的006A,也就是小写的字母"j",而之后的两字节用UTF8解码之后是0368,这个字符什么也不是。这就是只有"联通"两个字的文件没有办法在记事本里正常显示的原因。 

        而如果你在"联通"之后多输入几个字,其他的字的编码不见得又恰好是110和10开始的字节,这样再次打开时,记事本就不会坚持这是一个utf8编码的文件,而会用ANSI的方式解读之,这时乱码又不出现了。 

        在数据库中,有n前缀的字串类型就是Unicode类型,这种类型中,固定用两个字节来表示一个字符,无论这个字符是汉字还是英文字母,或是别的什么。 如果你要测试"abc汉字"这个串的长度,在没有n前缀的数据类型里,这个字串是7个字符的长度,因为一个汉字相当于两个字符。而在有n前缀的数据类型里,同样的测试串长度的函数将会告诉你是5个字符,因为一个汉字就是一个字符。

        以数字编号标识字符集编码,列表如下(常用编码用红色标出):

标识符名称备注37IBM037IBM EBCDIC(美国 - 加拿大)437IBM437OEM 美国500IBM500IBM EBCDIC(国际)708ASMO-708阿拉伯字符 (ASMO 708)720DOS-720阿拉伯字符 (DOS)737ibm737希腊字符 (DOS)775ibm775波罗的海字符 (DOS)850ibm850西欧字符 (DOS)852ibm852中欧字符 (DOS)855IBM855OEM 西里尔语857ibm857土耳其字符 (DOS)858IBM00858OEM 多语言拉丁语 I860IBM860葡萄牙语 (DOS)861ibm861冰岛语 (DOS)862DOS-862希伯来字符 (DOS)863IBM863加拿大法语 (DOS)864IBM864阿拉伯字符 (864)865IBM865北欧字符 (DOS)866cp866西里尔字符 (DOS)869ibm869现代希腊字符 (DOS)870IBM870IBM EBCDIC(多语言拉丁语 2)874windows-874泰语 (Windows)875cp875IBM EBCDIC(现代希腊语)932shift_jis日语 (Shift-JIS)936GBK简体中文 (GBK)949ks_c_5601-1987朝鲜语950big5繁体中文 (Big5)1026IBM1026IBM EBCDIC(土耳其拉丁语 5)1047IBM01047IBM 拉丁语 11140IBM01140IBM EBCDIC(美国 - 加拿大 - 欧洲)1141IBM01141IBM EBCDIC(德国 - 欧洲)1142IBM01142IBM EBCDIC(丹麦 - 挪威 - 欧洲)1143IBM01143IBM EBCDIC(芬兰 - 瑞典 - 欧洲)1144IBM01144IBM EBCDIC(意大利 - 欧洲)1145IBM01145IBM EBCDIC(西班牙 - 欧洲)1146IBM01146IBM EBCDIC(英国 - 欧洲)1147IBM01147IBM EBCDIC(法国 - 欧洲)1148IBM01148IBM EBCDIC(国际 - 欧洲)1149IBM01149IBM EBCDIC(冰岛语 - 欧洲)1200utf-16Unicode (UTF-16)1201unicodeFFFEUnicode (Big-Endian)1250windows-1250中欧字符 (Windows)1251windows-1251西里尔字符 (Windows)1252Windows-1252西欧字符 (Windows)1253windows-1253希腊字符 (Windows)1254windows-1254土耳其字符 (Windows)1255windows-1255希伯来字符 (Windows)1256windows-1256阿拉伯字符 (Windows)1257windows-1257波罗的海字符 (Windows)1258windows-1258越南字符 (Windows)1361Johab朝鲜语 (Johab)10000macintosh西欧字符 (Mac)10001x-mac-japanese日语 (Mac)10002x-mac-chinesetrad繁体中文 (Mac)10003x-mac-korean朝鲜语 (Mac)10004x-mac-arabic阿拉伯字符 (Mac)10005x-mac-hebrew希伯来字符 (Mac)10006x-mac-greek希腊字符 (Mac)10007x-mac-cyrillic西里尔字符 (Mac)10008x-mac-chinesesimp简体中文 (Mac)10010x-mac-romanian罗马尼亚语 (Mac)10017x-mac-ukrainian乌克兰语 (Mac)10021x-mac-thai泰语 (Mac)10029x-mac-ce中欧字符 (Mac)10079x-mac-icelandic冰岛语 (Mac)10081x-mac-turkish土耳其字符 (Mac)10082x-mac-croatian克罗地亚语 (Mac)12000utf-32Unicode (UTF-32)12001utf-32BEUnicode (UTF-32 Big-Endian)20000x-Chinese-CNS繁体中文 (CNS)20001x-cp20001TCA 台湾20002x-Chinese-Eten繁体中文 (Eten)20003x-cp20003IBM5550 台湾20004x-cp20004TeleText 台湾20005x-cp20005Wang 台湾20105x-IA5西欧字符 (IA5)20106x-IA5-German德语 (IA5)20107x-IA5-Swedish瑞典语 (IA5)20108x-IA5-Norwegian挪威语 (IA5)20127us-asciiUS-ASCII20261x-cp20261T.6120269x-cp20269ISO-693720273IBM273IBM EBCDIC(德国)20277IBM277IBM EBCDIC(丹麦 - 挪威)20278IBM278IBM EBCDIC(芬兰 - 瑞典)20280IBM280IBM EBCDIC(意大利)20284IBM284IBM EBCDIC(西班牙)20285IBM285IBM EBCDIC(英国)20290IBM290IBM EBCDIC(日语片假名)20297IBM297IBM EBCDIC(法国)20420IBM420IBM EBCDIC(阿拉伯语)20423IBM423IBM EBCDIC(希腊语)20424IBM424IBM EBCDIC(希伯来语)20833x-EBCDIC-KoreanExtendedIBM EBCDIC(朝鲜语扩展)20838IBM-ThaiIBM EBCDIC(泰语)20866koi8-r西里尔字符 (KOI8-R)20871IBM871IBM EBCDIC(冰岛语)20880IBM880IBM EBCDIC(西里尔俄语)20905IBM905IBM EBCDIC(土耳其语)20924IBM00924IBM 拉丁语 120932EUC-JP日语(JIS 0208-1990 和 0212-1990)20936x-cp20936简体中文 (GB2312-80)20949x-cp20949朝鲜语 Wansung21025cp1025IBM EBCDIC(西里尔 塞尔维亚 - 保加利亚)21866koi8-u西里尔字符 (KOI8-U)28591iso-8859-1西欧字符 (ISO)28592iso-8859-2中欧字符 (ISO)28593iso-8859-3拉丁语 3 (ISO)28594iso-8859-4波罗的海字符 (ISO)28595iso-8859-5西里尔字符 (ISO)28596iso-8859-6阿拉伯字符 (ISO)28597iso-8859-7希腊字符 (ISO)28598iso-8859-8希伯来字符 (ISO-Visual)28599iso-8859-9土耳其字符 (ISO)28603iso-8859-13爱沙尼亚语 (ISO)28605iso-8859-15拉丁语 9 (ISO)29001x-Europa欧罗巴38598iso-8859-8-i希伯来字符 (ISO-Logical)50220iso-2022-jp日语 (JIS)50221csISO2022JP日语(JIS- 允许 1 字节假名)50222iso-2022-jp日语(JIS- 允许 1 字节假名 - SO/SI)50225iso-2022-kr朝鲜语 (ISO)50227x-cp50227简体中文 (ISO-2022)51932euc-jp日语 (EUC)51936EUC-CN简体中文 (EUC)51949euc-kr朝鲜语 (EUC)52936hz-gb-2312简体中文 (HZ)54936GB18030简体中文 (GB18030)57002x-iscii-deISCII 梵文57003x-iscii-beISCII 孟加拉语57004x-iscii-taISCII 泰米尔语57005x-iscii-teISCII 泰卢固语57006x-iscii-asISCII 阿萨姆语57007x-iscii-orISCII 奥里雅语57008x-iscii-kaISCII 卡纳达语57009x-iscii-maISCII 马拉雅拉姆字符57010x-iscii-guISCII 古吉拉特字符57011x-iscii-paISCII 旁遮普字符65000utf-7Unicode (UTF-7)65001utf-8Unicode (UTF-8)


常用设置:

1、Windows系统默认的字符集是GBK,Linux系统默认的字符集是UTF-8;

2、JDK中的系统属性:file.encoding与文件内容的字符集有关,sun.jnu.encoding与文件名的字符集有关。JDK的部分版本如v1.4可用-D命令对该属性修改,但有的版本修改该属性不成功,解决方法可参考3;

3、在eclipse控制台输入汉字,print打印为乱码,但如果直接print打印字符串汉字显示正常。这种现象一般是由于printf打印时所采用平台的字符集与控制台显示汉字的字符集不一致所致(print打印过程实际是打印的字符转成字节传到控制台,控制台再转成字符进行显示),此时需要修改eclipse的初始化参数,即在eclipse安装根目录下的eclipse.ini文件中添加:-Dfile.encoding=UTF-8(windows默认是GBK)。

4、 在dos命令下编译java文件时报无法映射字符集,可使用-encoding utf-8参数指定字符集,也可以在环境变量中配置JAVA_TOOL_OPTIONS:-Dfile.encoding=UTF-8


转自:http://www.cnblogs.com/hongfei

0 0