首页 > 代码库 > 转:字符集和字符编码学习笔记

转:字符集和字符编码学习笔记

原文来自于:http://www.king-liu.net/146/

在web开发中我们总会遇到这样那样的字符编码问题,例如,当我们在代码编辑器里可以好好显示的html文档在浏览器里却变成了乱码,有时候为了能让我们的页面正常显示我们可能要忙上一天都无法解决(我可是深有体会)。为了搞清楚字符编码的问题,今天我也花了很长时间去百度。这里我和大家分享一下我的感想,不对之处,欢迎指正。

    首先要了解下什么是字符编码和字符集。

字符集(Charset):是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。
 

字符集的概念就是指一个集合,例如:26个英文字母就构成了一个英文字母字符集,还有繁体汉字字符集、日文汉字字符集等。

 

 字符编码(Character Encoding):是一套法则,使用该法则能够对自然语言的字符的一个集合(如字母表或音节表),与其他东西的一个集合(如号码或电脉冲)进行配对。即在符号集合与数字系统之间建立对应关系,它是信息处理的一项基本技术。通常人们用符号集合(一般情况下就是文字)来表达信息。而以计算机为基础的信息处理系统则是利用元件(硬件)不同状态的组合来存储和处理信息的。元件不同状态的组合能代表数字系统的数字,因此字符编码就是将符号转换为计算机可以接受的数字系统的数,称为数字代码。
 

字符编码指一套规则,计算机只能处理由0,1构成的数据,计算机不能处理符号和字符,他只会处理0,1数据。为了能让计算机处理这些非数值数据,我们就把非数值数据根据一定的规则与一些二进制数据对应起来,例如在ASCLL编码中,我们用‘0110 0001‘来表示‘a‘, ASCLL对应表 , 大家可以去看看。总而言之,这些将字符集里的字符与二进制数据对应的法则的集合就是字符编码。

所以,一个字符集必然对应着一个一个字符编码。

常见的字符集&字符编码。

ASCLL字符集&字符编码。
 

ASCIIAmerican Standard Code for Information Interchange,美国信息交换标准代码))是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语,而其扩展版本EASCII则可以勉强显示其他西欧语言。它是现今最通用的单字节编码系统(但是有被Unicode追上的迹象),并等同于国际标准ISO/IEC 646。ASCII字符集:主要包括控制字符(回车键、退格、换行键等);可显示字符(英文大小写字符、阿拉伯数字和西文符号)。

ASCII编码:将ASCII字符集转换为计算机可以接受的数字系统的数的规则。使用7位二进制(一个字节)表示一个字符,共128字符。后来,随这计算机的普及128个字符不够用了,于是就动用了剩下的哪一个二进制,形成了扩展的ASCLL编码,共有256个。

128的ASCLL编码表

256的ASCLL编码表。

ASCLL相对来说是一种比较古老的字符集&字符编码,因为他表示的字符太少了,渐渐就产生了其他包含更多字符的字符集&字符编码。

GBXXXX字符集&编码
 

BG—-国标
 

GB 2312 字符集&编码
 

GB 2312 字符集:
GB 2312 或 GB 2312-80 是中国国家标准简体中文字符集,全称《信息交换用汉字编码字符集·基本集》,又称GB0,由中国国家标准总局发布,1981年5月1日实施。GB2312编码通行于中国大陆;新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化的软件都支持GB 2312。
 

GB 2312字符编码:
一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到 0xF7(176–247),后面一个字节(低字节)从0xA1到0xFE(160–254),这样我们就可以组合出大约6654多个简体汉字了。在这些编码里,还把数学符号、罗马希腊的 字母、日文的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。
 

GB2312字符集中删掉了扩展后的那部分ASCLL字符,也就是说一个字符的二进制大于127是没有在GB2312中定义的。GB2312 收录了%99的常用汉字,但对于一些偏僻的姓,少数名族的字符没有收录,这时候就有必要出现另一种扩充的编码方式,这就是GBK。

GBK字符集&编码
 

GBK字符集: gbk字符集是GB2312的超集,同 GB2312一样,GBK也支持希腊字母、日文假名字母、俄语字母等字符,但不支持韩语中的表音字符(非汉字字符)。GBK还收录了GB2312不包含的 汉字部首符号、竖排标点符号等字符。

GBK字符编码:为了能够表示更多的字符,GBK的制定者们扩充了表示字符的字节范围,规定:一个小于127的字符的意义与原来相同,当有一个大于127的字符出现时且这个大于127的字符的后面跟着一个字节范围是0x40-7E和0x80-0xFE(十进制:64—127和128—256)时,他就表示一个汉字,也就是说高字节范围是0×81-0xFE,低字节范围是0x40-7E和0x80-0xFE。从这一点可以看出,GBK完全兼容了GB2312,而他又进行了扩展,但这个扩展对程序员们造成了一些影响。在GBK中,当我们遍历一个字符串时,我们无法通过判断他的二进制码是否小于127来判断他是有特殊含义的ASCLL字符,因为他有可能是一个汉字的一半。所以,当我们想要在字符串中添加标识符时,我们最好选择小于64的字符来做,例如(!,@,#,$,%,^).

有些系统中用0x40-0x7E中的字符(如"|")做特殊符号,在定位这些符号时又没有判断这些符号是不是属于某个 GBK字符的低字节,这样就会造成错误判断。在支持GB2312的环境下就不存在这个问题。需要注意的是支持GBK的环境中小于0x80的某个字节未必就 是ASCII符号;另外就是最好选用小于0×40的ASCII符号做一些特殊符号,这样就可以快速定位,且不用担心是某个汉字的另一半。Big5编码中也存在相应问题。

GB13080字符集&编码
 

GB13080字符集:全称:国家标准GB 18030-2005《信息技术 中文编码字符集》,是中华人民共和国现时最新的内码字集,是GB 18030-2000《信息技术 信息交换用汉字编码字符集 基本集的扩充》的修订版。与GB 2312-1980完全兼容,与GBK基本兼容,支持GB 13000及Unicode的全部统一汉字,共收录汉字70244个。

GB13080字符编码:GBK和GB2312都是双字节等宽编码,如果算上和ASCII兼容所支持的单字节,也可以理解为是单字节和双字节混合的变长编码。GB18030编码是变长编码,有单字节、双字节和四字节三种方式。GB18030的单字节编码范围是0x00-0x7F,完全等同与ASCII;双字节编码的范围和GBK相同,高字节是0x81-0xFE,低字节 的编码范围是0x40-0x7E和0x80-FE;四字节编码中第一、三字节的编码范围是0x81-0xFE,二、四字节是0x30-0x39。

BIG5字符集&编码

BIG5字符集:由于GB系列的字符集&字符编码只收录了简体汉字而没有考虑台湾人民的感受,所以他们也推出了自己的字符集&字符编码那就是大五码—BIG5,Big5收录的汉字只包括繁体汉字,不包括简体汉字,一些生僻的汉字也没有收录。GBK收录的日文假名字符、俄文字符BIG5也没有收录。Big5编码对应的字符集是GBK字符集的子集,也就是说Big5收录的字符是GBK收录字符的一部分,但相同字符的编码不同。

BIG5字符编码:Big5是双字节编码,高字节编码范围是0x81-0xFE(128–255),低字节编码范围是0x40-0x7E和0xA1-0xFE。和GBK相比,少了低字节是0x80-0xA0的组合。0x8140-0xA0FE是保留区域,用于用户造字区。

因为Big5也占用了ASCII的编码空间(低字节所使用的0x40-0x7E),所以Big5编码在一些环境下存在和GBK编码相同的问题,即低字节范围为0x40-0x7E的字符有可能会被误处理,尤其是低字节是0x5C("/")和0x7C("|")的字符。可以参考GBK一节相应说明。

小结:在上述所例举的GB系列,BIG5,这些字符集他们都用一个小于127的字节来表示英文字符,用两个字节且第一个字节必须大于127的双字节来表示汉字。于是他们有了一个共同的名字—- "DBCS"(Double Byte Charecter Set 双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。

伟大的UNICODE

 因为当时各个国家都像中国这样搞出一套自己的编码标准,结果互相之间谁也不懂谁的编码,谁也不支持别人的编码,连大陆和台湾这样只相隔了150海里,使用 着同一种语言的兄弟地区,也分别采用了不同的 DBCS 编码方案。导致文件在不同的地区的传输出现了很大的问题,这时候一个伟大的创想产生了——Unicode("Universal Multiple-Octet Coded Character Set",简称 UCS, 俗称 "UNICODE")。这个由一个叫 ISO(国际标谁化组织)的国际组织编制的字符集可以表示世界上所有的字符,它规定用4个字节来表示字符,每个数字代表唯一的至少在某种语言中使用的符号(并不是所有的数字都用上了,但是总数已经超过了65535,所以2个字节的数字是不够用的。)在UNICODE中,每个字符对应一个数字,每个数字对应一个字符。即不存在二义性。不再需要记录"模式"了。U+0041总是代表‘A‘,即使这种语言没有‘A‘这个字符。

在计算机科学领域中,Unicode(统一码、万国码、单一码、标准万国码)是业界的一种标准,它可以使电脑得以体现世界上数十种文字的系统。Unicode 是基于通用字符集(Universal Character Set)的标准来发展,并且同时也以书本的形式[1]对外发表。直至目前为止的第六版,Unicode 就已经包含了超过十万个字符(在2005年,Unicode 的第十万个字符被采纳且认可成为标准之一)、一组可用以作为视觉参考的代码图表、一套编码方法与一组标准字符编码、一套包含了上标字、下标字等字符特性的枚举等。Unicode 组织(The Unicode Consortium)是由一个非营利性的机构所运作,并主导 Unicode 的后续发展,其目标在于:将既有的字符编码方案以Unicode 编码方案来加以取代,特别是既有的方案在多语环境下,皆仅有有限的空间以及不兼容的问题。

UNICODE这种编码方式虽然形成了一种统一的字符集,世界上的人们终于能在同一种字符集下工作了。但是他也带来了一些问题。

例如汉字"严"的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。而计算机不知道这两个字节是一个汉字,还是两个ASCLL字符。

用跟多的字节表示英文字符,这样浪费了大量的空间,老外们非常不高兴。

传输时的问题。

于是这个时候就有必要出现一种可以解决这个问题的全新的编码方式。于是就出现了UTF系列(utf-8,utf-16,utf-32)

可以这样理解:Unicode是字符集,UTF-32/ UTF-16/ UTF-8是三种字符编码方案。
 

UTF-32编码方式:
 

上述使用4字节的数字来表达每个字母、符号,或者表意文字(ideograph),每个数字代表唯一的至少在某种语言中使用的符号的编码方案,称为UTF-32。UTF-32又称UCS-4是一种将Unicode字符编码的协定,对每个字符都使用4字节。就空间而言,是非常没有效率的。这种方法有其优点,最重要的一点就是可以在常数时间内定位字符串里的第N个字符,因为第N个字符从第4×Nth个字节开始。虽然每一个码位使用固定长定的字节看似方便,它并不如其它Unicode编码使用得广泛。

UTF-16 编码方式

尽管有Unicode字符非常多,但是实际上大多数人不会用到超过前65535个以外的字符。因此,就有了另外一种Unicode编码方式,叫做UTF-16(因为16位 = 2字节)。UTF-16将0–65535范围内的字符编码成2个字节,如果真的需要表达那些很少使用的"星芒层(astral plane)"内超过这65535范围的Unicode字符,则需要使用一些诡异的技巧来实现。这些诡异的技巧请自行百度。

小结:
对于UTF-32和UTF-16编码方式还有一些其他不明显的缺点。不同的计算机系统会以不同的顺序保存字节。这意味着字符U+4E2D在UTF-16编码方式下可能被保存为4E 2D或者2D 4E,这取决于该系统使用的是大尾端(big-endian)还是小尾端(little-endian)。这时候数据的传输就出了问题,为了解决这个问题,多字节的Unicode编码方式定义了一个"字节顺序标记(Byte Order Mark)",它是一个特殊的非打印字符,你可以把它包含在文档的开头来指示你所使用的字节顺序。对于UTF-16,字节顺序标记是U+FEFF。如果收到一个以字节FF FE开头的UTF-16编码的文档,你就能确定它的字节顺序是单向的(one way)的了;如果它以FE FF开头,则可以确定字节顺序反向了。这就是我们在notepad++的格式选项里看到的UCS-2 BIG endian & UCS-2 Little endian 的含义了。

UTF-8 编码方式

互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种Unicode的实现方式,也是最重要的一种编码方式。

UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码(定长码),也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

UTF-8的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

下表总结了编码规则,字母x表示可用编码的位。

Unicode符号范围 | UTF-8编码方式

(十六进制) | (二进制)

——————–+———————————————

0000 0000-0000 007F | 0xxxxxxx

0000 0080-0000 07FF | 110xxxxx 10xxxxxx

0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx

0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

下面,还是以汉字"严"为例,演示如何实现UTF-8编码。

已知"严"的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此"严"的UTF-8编码需要三个字节,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然后,从"严"的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,"严"的UTF-8编码是"11100100 10111000 10100101",转换成十六进制就是E4B8A5。

从上面的过程中我们可以看出在utf-8中 汉字大多是三个字节的而英文字母都是1个字节的,这样原来的英文文件就可以在不用修改的情况下来适应utf-8,也不用多余的占用空间。

UTF-8优点:

  1. UTF-8是ASCII的一个超集。因为一个纯ASCII字符串也是一个合法的UTF-8字符串,所以现存的ASCII文本不需要转换。为传统的扩展ASCII字符集设计的软件通常可以不经修改或很少修改就能与UTF-8一起使用。
  2. UTF-8字符串可以由一个简单的算法可靠地识别出来。就是,一个字符串在任何其它编码中表现为合法的UTF-8的可能性很低,并随字符串长度增长而减小。

Utf-8 BOM 问题:

前面提到过"字节顺序标记(Byte Order Mark)"—-BOM 这是用来表示UTF-16和UTF-32的字节顺许的,但UTF-8并不需要他,他在utf-8文件中被编译为" EF BB BF ",占三个字节。这就是我们用记事本创建一个utf-8文件时,它的起始大小为3字节,这三个字节来标识UTF-8编码。但这三个字节会常常造成一些问题。

例如:

  1. 当一个文件流是utf-8 BOM 形式编码的话,其在除IE10之外的IE中都会输出一个空行。这会导致一些页面的错乱。
  2. 受COOKIE送出机制的限制,在这些文件开头已经有BOM的文件中,COOKIE无法送出(因为在COOKIE送出前PHP已经送出了文件头),所以登入和登出功能失效。一切依赖COOKIE、SESSION实现的功能全部无效。

解决办法:

使用notepad++编辑器打开文件,在"格式"选项中选择"转为UTF-8无BOM格式"。

ANSI 是神马??

在notepad++中的"格式"选项中,我们可以看到"以ANSI编码"这个选项,那么,ANSI究竟是什么呢???

为使计算机支持更多语言,通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。比如:汉字 ‘中‘ 在中文操作系统中,使用 [0xD6,0xD0] 这两个字节存储。不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。

以上摘自百度百科。。。

ANSI 就是一种对双字节编码方式的总称,我们可以把它看做一个变量,在不同的地域,他的值是不同的。而这个地域的判断又是依赖于操作系统。在中国,ANSI=GB2312,在日本,ANSI=JIS。有的时候ANSI也被称为‘本地编码‘。事实上,ANSI的值取决于windows的‘codepage‘,你可以在命令行下输入"chcp"来查看你的codepage,一般为936,然后查看cmd的属性时,你会发现codepage时GBK。

说了半天,也就是在中国,你的ANSI就是GB2312。文本以ANSI的形式存储或处理时,就是以GB2312处理的。

举个例子:

比 如 一个用户在简体中文Windows下面用记事本输入一些汉字后保存,然后copy到另一台英文Windows上,尝试用记事本打开,就会发现出现的是一 些乱码。并不是copy的过程中出现了错误,而是因为在英文的Windows上打开文件时默认的ANSI编码是Wenstern European(Windows) – Codepage 1252,而在简体中文的Windows上保存打开时默认的编码格式是Chinese Simplified(GB2312) – Codepage 936。

实验课

新建5个txt文件分别命名为"utf-8.txt","gb2312.txt","ANSI.txt","Unicode big endian,txt","Unicode little endian.txt",然后用notepad++打开,将其改变为名称对应的编码方式。然后在里面都输入‘严‘

然后,使用Ultraedit 打开,按下‘ctrl+H‘查看他们的十六进制码。

    • ANSI:文件的编码就是两个字节"D1 CF",这正是"严"的GB2312编码。:
    • Gb2312:文件的编码就是两个字节"D1 CF",和ANSI一样。
    • Unicode litte endian:编码是四个字节"FF FE 25 4E",其中"FF FE"表明是小头方式存储,真正的编码是4E25。
    • Unicode big endian:编码是四个字节"FE FF 4E 25",其中"FE FF"表明是大头方式存储。
    • UTF-8:编码是六个字节"EF BB BF E4 B8 A5",前三个字节"EF BB BF"表示这是UTF-8编码,后三个"E4B8A5"就是"严"的具体编码,它的存储顺序与编码顺序是一致的。

       
       

       
       

      参考资料及延伸阅读:

      http://blog.csdn.net/stilling2006/article/details/4129700

      http://www.pconline.com.cn/pcedu/empolder/gj/other/0505/616631.html

      http://www.joelonsoftware.com/articles/Unicode.html

      http://www.crifan.com/files/doc/docbook/char_encoding/release/webhelp/content/

      http://huaichang.blogbus.com/logs/19602597.html

转:字符集和字符编码学习笔记