透彻理解字符集和字符编码,彻底走出乱码沼泽

        只要有中文的地方就会有编码问题......

        编码问题是学习编程中非常重要的一个问题,如果对编码和字符集不能透彻理解,那么就如同行走在沼泽,随时会让程序员深陷其中、无法自拔。字符编码这个知识非常基础又非常重要,但现实是,很多公司里即便从事多年开发的程序员也常常对此一知半解。遇到问题时大多连蒙带猜地解决,不求甚解。

        工作中最常出现编码问题的地方:

 1、无论前端开发、java开发、PHP开发、移动开发还是Python开发,任何一种计算机语言都会遇到字符编码问题;

2、表单提交数据到服务器时,容易出现数据传输错误;

3、只要涉及到网络传输中文数据,极其出现编码问题;

4、当用到Ajax技术时,经常出现乱码问题;

5、非编程人员日常生活中也常出现编码问题。比如打开文件后显示乱码,如果理解字符编码,只需要换一种打开方式,或许问题就迎刃而解。        

总之,字符编码问题很重要,不掌握将后患无穷。



一、字符集及字符编码概述

(一)、基本概念

1、首先介绍:字符、字符集、字符编码三者的含义。

(1)、字符:是关于文字和符号的总称,包括各个国家的文字、标点符号、图形符号、数字等等。例如:一个汉字,一个标点符号逗号,一个英文字母,一个数字,这都是字符。

(2)、字符集:是多个字符的集合。我们可以理解成就是一本大字典。字符集种类很多,每个字符集包含的字符个数也不同,常见的字符集有:ASCII字符集、Unicode字符集、GB2312字符集、 ISO-8859-1字符集等等。

(3)、字符编码:计算机只能识别二进制代码1和0。字符集中的字符,计算机是不能直接识别的,所以要将字符转化为计算机可以识别的二进制码,计算机才能认识,这个转化过程就是编码,而字符编码就是将二进制码与字符集中的字符对应起来的一套规则。

        请牢记:字符集就是字典,而字符编码就是计算机查找字典的规则。

        各个国家和地区在制定编码标准的时候,字符集和编码规则都是同时制定的。因此,平常我们所说的字符集,如ASCII、GB2312、GBK、Unicode等,除了有字符集合的含义外,同时也包含字符编码的含义。

         一般来说,每种字符集都有相应的一种字符编码规则。

        如ASCII码字符集只有一种编码方式,就是ASCII编码;

        GB2312字符集只有一种编码方式,就是GB2312编码。

        但有的字符集有多种编码方式,比如,Unicode字符集有UTF-8、UTF-16、UTF-32等多种字符编码方式。


2、其次介绍字符与字节的含义。

        不要将字符与字节搞混。字符是文化符号,而字节是文件的长度单位。

比如有一个文件,内容如下:“ABC123”

在这个文件中,我们输入的是“半角”的“ABC123”,一共就包含6个字符。但是这个文件的大小有多少字节呢?

        答案是:6字节、9字节、12字节、14字节、24字节、28字节

        没有想到吧。怎么会有这么多种答案呢?其实只要掌握了字符集及字符编码就明白了。不同的字符集,不同的编码方式,都会导致文件的大小不同。


(二)、回顾计算机基本原理

计算机最基本的硬件是半导体芯片,每块芯片集成了数万到数百万个晶体管;

计算机最基本的物理元件是晶体管,每一个晶体管具有开和关两种状态;

七个晶体管组合在一起就可以组合出128种不同的状态(=2的7次方);

八个晶体管组合在一起就可以组合出256种不同的状态(=2的8次方)。

规律:2的n次方



二、ASCII码

(一)、ASCII码的由来

ASCII码(American Standard Code for Information Interchange),它是“美国标准信息互换码”;

ASCII码是由美国国家标准学会(American National Standard Institute , ANSI )制定的,标准的单字节字符编码方案,用于基于文本的数据。

起始于50年代后期,在1967年定案。它最初是美国国家标准,供不同计算机在相互通信时用作共同遵守的西文字符编码标准,它已被国际标准化组织(International Organization for Standardization, ISO)定为国际标准,称为ISO 646标准。适用于所有拉丁文字字母。

美国国家标准学会(American National Standards Institute: ANSI)。美国国家标准学会成立于1918年。当时,美国的许多企业和专业技术团体,已开始了标准化工作,但因彼此间没有协调,存在不少矛盾和问题。为了进一步提高效率,数百个科技学会、协会组织和团体,均认为有必要成立一个专门的标准化机构,并制订统一的通用标准。

计算机每个晶体管可以存储的两种状态,构成了计算机最小的存储单位:位(bit,比特);

七个晶体管组合出的128种不同的状态,每种状态保存一个字节,这就构成了“标准的7位ASCII码”;

128种状态足够保存美国所有的文化符号(大写26个字母、小写26个字母、0-9阿拉伯数字、所有的英文标点符号、数学运算符号、其他特殊符号以及控制码);

ASCII码中编号0~31是非打印字符,用于控制字符的换行回车删除等。也统称为"控制码";32~126 是打印字符,可以通过键盘输入并且能够显示出来。

编号48至57,保存的是数字0到9;

编号65至90,保存的是大写字母A到Z;

编号97至122,保存的是小写字母a到z;

其它位置保存的是标点符号、数学运算符号及特殊符号。

计算机起初是美国用于军事而发明的产物,后来更多国家开始使用计算机,这些国家的字母是ASCII码中没有的。后来IBM公司在此基础上进行了扩展,用8bit来表示一个字符,总共可以表示256个字符(八个晶体管可以组合256种不同的状态)。当第一位是0时仍然表示7位ASCII码的字符,当第一位为1时就表示新补充的字符。

编号128到编号255的字符集被称为“扩展ASCII码”,也就是八位ASCII码。

(二)、ASCII码的编号及对应字符


备注:

二进制     binary

八进制     octet(8位字节)

十进制    decimal

十六进制   hexadecimal



三、中文字符集

(一)、GB2312字符集

        中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且中文常用字就有6000多个。那么该如何办呢?

        规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,这就是“GB2312字符集”,这是由中国国家标准总局1980年发布的。

        GB2312字符集中依然保留了前128个ASCII码的字符。而把编号在127号之后的符号们全部去掉。

        GB2312 是对ASCII 编码的中文扩展,ASCII码是典型的单字节编码,一个字符就占一个字节长度。因此GB2312字符集中,前128个字符是单字节的,编号在127之后的GB2312编码则是双字节的,也就是一个字符是两个字节长度。

        GB2312字符集只支持6763个简体汉字。GB2312字符集,除汉字外还把数学符号、罗马字母、希腊字母、日文假名都编进去了。

        GB2312字符集还将 ASCII 里本来就有的大小写英文字母、阿拉伯数字、标点符号重新编了一套两个字节长的编码,这就是“全角”字符,而原来在127号以下的那些就叫“半角”字符了。    全角和半角只针对于字母、数字和标点而言的,对于中文字是无所谓全角和半角区分的。

        “全角”字符是双字节编码,而“半角”字符是单字节编码,因此长度上就能看出不同,全角字符要比半角字符胖一倍。例如:

 abcdefgABCDEFG1234567890

  abcdefgABCDEFG1234567890

(二)、GBK 字符集

        中国的汉字太多,GB2312字符集中的6763个汉字不够使用,1993年又颁布了《汉字编码扩展规范》,也就是GBK 编码。GBK就是国标扩展的意思。GBK 包括 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。

(三)、GB18030字符集

        随着中国各少数民族使用电脑,中文字符集继续扩展。国家标准GB18030-2000《信息交换用汉字编码字符集基本集的补充》是我国继GB2312-1980和GB13000-1993之后最重要的汉字编码标准,GB18030字符集又加了几千个新的少数民族的文字。GB18030-2000是GBK的取代版本,它的主要特点是在GBK基础上增加了CJK(china-japan-korea,中日韩)统一汉字扩充的汉字。

(四)、DBCS字符集

        GB2312、GBK、GB18030等一系列汉字编码,中国程序员通称他们叫做 "DBCS"(Double Byte Charecter Set,双字节字符集)。其最大的特点是:除字符集中包含的ASCII码字符外,其它的每个字符都占两个字节长度。

        如果有人问GBK字符集中,一个字占几个字节?千万别张嘴就来:两个字节。

        别忘记,GBK字符集中包含128个单字节的ASCII码。

        如果有人问GBK字符集中,一个汉字占几个字节?那可以回答:两个字节。



四、Unicode字符集(UCS,统一字符集、万国码、单一码)

(一)、出现的原因

1、当时各个国家都像中国这样搞出一套自己的编码标准,结果相互之间谁也不懂谁的编码,谁也不支持别人的编码,甚至中国大陆和中国台湾这样只相隔了150海里,使用着同一种语言的兄弟地区,也采用了不同的编码方案。

        在1994年之前,也就是Unicode编码出现之前,大陆地区的中国人想使用台湾软件,还必须加装一套支持 BIG5 编码的"倚天汉字系统"才可以用,装错了字符系统,显示就会乱码。

2、 ISO (国际标准化组织   International Organization for Standardization)组织着手解决编码不统一的问题。

        ISO组织舍弃了所有地区性的编码方案,重新搞了一个包括了地球上所有文化、所有字母和符号的编码。叫做"Universal Multiple-Octet Coded Character Set",(字面意思:全球通用的多八位字节字符集,中文名称为:统一字符集)简称UCS,俗称 "Unicode"。

        Unicode是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。Unicode编码的出现,其实就是为了适应全球化的发展,便于不同语言之间的兼容交互。

(二)、Unicode字符集的特点

1、Unicode 开始制订时,计算机的存储器容量极大地发展,空间再也不成为问题。

        Unicode字符集中,ISO 直接规定必须用两个字节(UCS-2方案),也就是16位来统一表示所有的字符;

        对于ASCII码中的字符,Unicode 保持其原编码不变,但是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码;

        由于"半角"英文符号只需要用到低8位,所以其高8位永远是0来补齐,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。

2、Unicode 是用两个字节来表示一个字符,总共可以组合出65536不同的字符

        这大概已经可以覆盖世界上所有文化的符号。但是Unicode对汉字支持其实依然不足够好,因为简体和繁体汉字总共有六七万个汉字,而UCS-2最多能表示65536个,所以Unicode只能排除一些几乎不用的汉字,好在常用的简体汉字也不过七千多个。为了能表示所有的汉字,ISO已经准备了UCS-4方案,给Unicode增加了UCS-4规范,就是用四个字节来表示一个字符,这样就可以保存42亿个不同的字符了!

3、Unicode字符集中包含中文字符20902个。

        Unicode大字典包含了世界上所有的文化符号,其中第一个汉字就是“一”(位置编号为19968,十六进制表示为:\u4E00),最后一个汉字是“龥”(位置编号40869,十六进制表示为:\u9FA5)。

        请大家牢记:[\u4E00-\u9FA5]。

        [\u4E00-\u9FA5]其实就是中文的正则表达式,它表示Unicode字符集中第一个中文字符到最后一个中文字符的意思。换句话说它是所有中文字的表达式。这在以后的工作中经常用到,尤其是javascript进行表单的验证中。

【备注】

“龥”字同【吁】字,有三个发音。    

a、拼音xu1。

叹息:长~短叹。同长吁短叹

b、拼音yu4【吁】的简体字

为某种要求而呼喊:呼~。同呼吁

c、拼音yu1

象声字,哟喝牲口停止前进的声音


4、Unicode编码与GBK系列编码最大的区别

        Unicode编码虽然与GBK都是双字节编码,但是Unicode编码中每个字符都是两个字节,即便是原ASCII码中的字符也是两个字节;

        而GBK编码中依然保留原ASCII码中的字符为单字节,除此之外的字符才是双字节。

5、Unicode 在制订时没有考虑与任何一种现有的编码方案保持兼容

        因此 GBK 与Unicode 在汉字的内码编排上完全是不一样的,没有一种简单的算术方法可以把文本内容从Unicode编码和另一种编码进行转换,这种转换必须通过查表来进行。



五、Unicode字符集的字符编码规则

(一)、UTF编码

1、事实证明,对可以用ASCII表示的字符使用Unicode并不高效,因为Unicode比ASCII占用大一倍的空间。

         一个ASCII中的数字和字母,明明可以用八位0和1就表示清楚,Unicode非要用16位来表示。多余的那8位(也就是高字节)用0来补齐,对ASCII码来说高字节的0毫无用处,白白浪费了空间。

        为了保证在网络传输中减少不必要的流量浪费,提升文本传输速度,出现了一些中间格式的字符编码,他们被称为统一的转换格式,即UTF系列编码(Unicode Transformation Format)。UTF是Unicode字符集的编码方式。

        常见的UTF编码规则有:UTF-8、UTF-16、UTF-32。无论是UTF-8还是UTF-16都是处理Unicode字符集的,但是它们的编码规则不相同。

(二)、UTF-16编码

1、UTF-16比较好理解,就是任何字符都用两个字节来保存。

        Unicode在定义时就是两个字节表示一个字符,因此常常将Unicode与UTF-16等同对待;

        UTF-16编码效率最高,字符到字节相互转换更简单,进行字符串操作也更好。它适合在本地磁盘和内存之间使用,可以进行字符和字节之间的快速切换,如Java的内存编码就采用UTF-16编码;

        但是UTF-16不够经济,如果文档中都是英文字母,那么这个做法就非常浪费。明明用一个字节就能保存的偏用两个字节来保存。尤其在现在的网络带宽还有限的今天,使用UTF-16编码会增大网络传输的流量,很没必要。所以UTF-16编码不适合在网络之间传输。

(三)、UTF-8编码

1、UFT-8编码是Unicode字符集最常用的编码方式。

        UTF-8比UTF-16的解码要复杂。UTF-8编码是可变字节编码。

        文本读取是一个字节一个字节的来读取,然后再根据字节中开头的bit标志来识别,然后确定几个字节是一个字符单元。

        UTF-8编码规则中,原Unicode前128个字符是单字节编码(实体编号在127以内),编号在128至2047的是双字节编码(2的11次方=2048),编号在2048之后就是三字节编码;

一个字节代表一个字符:第一个字节为0。

两个字节代表一个字符:第一个字节的前3位是110,第2个字节的前2位是10,说明两个字节代表一个字符;

三个字节代表一个字符:第1个字节的前4位是1110,第2个字节的前2位是10,第3个字节的前2位是10,说明三个字节代表一个字符。如下图:


UTF-8文件中,ASCII码占一个字节,中文字则占三字节长度。

相比较UTF-16而言,UTF-8更适合网络传输。

对ASCII字符采用单字节存储,最大程度低节省文件传输时的流量大小。

单个字符损坏也不会影响后面的其他字符,编码安全性强。

在编码效率上介于GBK和UTF-16之间。

因此,UTF-8在编码效率上和编码安全性上做了平衡,是理想的中文编码方式。


(四)、UTF-8+编码(UTF-8  with BOM)

1、BOM  (Byte Order  Mark ,字节序列标记)

如果用记事本把一个文本文件另存为UTF-8编码方式的话,用ultraEditor打开这个文件,切换到十六进制编辑状态就可以看到开头的EF BB BF了。这是个标识UTF-8编码文件的好办法。如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码的文件。所以UTF-8可以用BOM来表明编码方式。

其实EF BB BF的二进制编码为:

        EF    11101111

        BB    10111011

        BF    10111111

        转成Unicode二进制编码为:1111111011111111

        十进制编号为:65279

        在html页面上输出为:空格

2、UTF-8编码的文件中,BOM占三个字节。

(五)、UTF-16 with BOM编码

1、保存的文件类型为UTF-16,那么开头的字节流是:FF  FE(表示UTF-16)。

FF FE的二进制编码为:11111111  11111110

Unicode二进制编码同上。

        十进制编号为:65534

        在html页面上输出为:空格

2、UTF-16编码的文件中,BOM占两个字节。


        截止到现在,我们讲了ASCII编码、GB2312、GBK、UTF-8、UTF-8+、UTF-16、UTF-16+编码。此时再问答以下问题,你是否足够清晰了呢?

有一个文件,内容如下:

“ABC123”

在这个文件中,我们输入的是“半角”的“ABC123”,一共包含6个字符。这个文件占多少个字节呢?

        你是否能分析出,为什么答案可以是:6字节、9字节、12字节、14字节、24字节、28字节?



六、其它编码或字符集

(一)、ANSI编码 

        ANSI是一种字符编码标准,其实就是各个国家或地区的国标。对于中国地区就是GB2312编码,中国台湾地区就是BIG5编码,对于日本就是JIS编码,对于美国自然就是ASCII码等等。

        windows操作系统中的记事本,默认保存的编码就是ANSI编码(在中国就是GB2312)    

(二)、ISO-8859-1字符集

        标准ASCII码只有128 个字符,后来变成了国际标准ISO-646。但是很显然ASCII码实在太少,远远不够用,于是 在 ASCII 码基础上又制定了一系列标准用来扩展 ASCII 编码,它们是 ISO-8859-1~ISO-8859-15。其中 ISO-8859-1 涵盖了大多数西欧语言字符,应用的最广泛。

        ISO-8859-1 仍然是单字节编码,它总共能表示256个字符。

        Linux操作系统默认的就是ISO-8859-1编码; 而Win32操作系统的机器默认是GB2312编码。

(三)、结束语

        常见的字符集有 ASCII(ISO-646)、ISO-8859-1、GB2312、GBK、Unicode等,它们都可以被看作是不同文化的大字典。字符编码规定了字符转化的规则,按照这个规则计算机就能正确地识别字符。

        目前存储中文的编码格式很多,例如 GB2312、GBK、UTF-8、UTF-16 这几种格式都可以存储汉字,那到底选择哪种编码格式来保存文件呢?这就要综合考虑、有所取舍,要考虑到底是存储空间重要还是编码效率更重要,从而正确选择编码格式。

        似乎听起来挺复杂的样子,其实当我们保存文件时,首选都是保存为UTF-8编码格式。

你可能感兴趣的:(透彻理解字符集和字符编码,彻底走出乱码沼泽)