尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

一文读懂字符编码

一文读懂字符编码 文章目录1.导语2.ASCII3.Latin14.ANSI5.中文编码GB2312GB13000、GBK、GB18030 的由来Big5 的由来GBK、GB18030、Big5与ANSI的关系小结6.Unicode 与 BMP7.UCS、UCS-2 与 UCS-48.UTF-8、UTF-16与UTF-328.1 UTF-88.2 UTF-168.3 UTF-329.Little Endian、Big Endian与BOM10.内码、外码与代码页Code Page11.后记参考文献一文带你了解 ASCIILatin1ANSIUnicodeUCS-2UCS-4UTF-8UTF-16UTF-32GB2312GB13000GBKGB18030BIG5BMPCode PageBOMMBCSLittle EndianBig Endian内码外码。1.导语字符编码Character Encoding是计算机显示文本的基础是每一位 IT 从业者必知的计算机基础知识点如同数值在计算中如何存储表示那么基础那么重要。然因字符编码历史久远变更频繁地域差别参考文献内容不全质量参差不齐等原因让不少读者望而却步坚持刨根究底的读者最终也难免云里雾里不知所以然。鉴于此本文将尝试带领大家弄清楚字符编码相关术语的概念各自间的联系和区别不足之处请读者批评指正不甚感激。关于字符编码的介绍网上已经有很多前人留下了值得参考的文章这里推荐几篇建议在阅读本篇博文前请大家研读以下几篇文章阅读顺序不作要求。字符编码笔记ASCIIUnicode和UTF-8各种编码UNICODE、UTF-8、ANSI、ASCII、GB2312、GBK详解闲谈字符和字符集以及编码上闲谈字符和字符集以及编码下字节那些事儿彻底搞懂字符编码(unicode,mbcs,utf-8,utf-16,utf-32,big endian,little endian…)2.ASCII我们知道计算机存储数据都是以二进制形式存储的以字节Byte为最小存储单位以比特Bit为最小状态。每一个 Bit 取值为 0 或 1 两种状态每一个字节有8个Bit位也就是一个字节可以表示256种状态。那计算机是如何存储和识别 0 和 1 这两种状态的呢计算机中 0 和 1 分别由低电平低电压和高电平高电压表示实现的硬件基础就是晶体二极管原理就是利用了晶体二极管的单向导电性。ASCIIAmerican Standard Code for Information Interchange美国信息交换标准代码是基于拉丁字母的一套电脑编码系统。上个世纪60年代由美国制定的一套字符编码将英语字符与二进制位之间做了统一规定。主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统。它已被国际标准化组织ISO定为国际标准称为ISO/IEC 646。ASCII 编码一共规定了 128 个字符的编码比如空格Space码值是 32二进制 00100000大写字母A码值是 65二进制 01000001。这128个符号包括32个不能打印出来的控制符号只占用了一个字节的后面7位最前面的 1 位统一规定为 0。3.Latin1Latin1 是国际标准编码 ISO-8859-1 的别名。Latin1也是单字节编码在 ASCII 编码的基础上利用了 ASCII 未利用的最高位扩充了128个字符因此Latin1可以表示256个字符并向下兼容 ASCII。Latin1 收录的字符除ASCII收录的字符外还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。欧元符号出现的比较晚没有被收录在 ISO-8859-1 当中在后来的修订版 ISO-8859-15加入了欧元符号。Latin1 编码范围是 0x00-0xFFASCII 编码范围是 0x00-0x7F。Latin1相对ASCII而言较少被提及其实Latin1的使用还是比较广泛的比如MySQL的数据表存储默认编码就是Latin1。4.ANSI说到 ANSI大家也许会认为是美国国家标准委员会American National Standards Institute但是本文讨论的是字符编码此处的 ANSI 是指字符编码。每个国家和地区为了表示自己的文字字符各自制定了不同的编码标准由此产生了GB2312、GBK、GB18030、Big5、Shift_JIS等不同的编码。ANSI 编码不是单一明确的字符编码是对不同国家和地区不同编码的一个统称根据当前系统的语言环境采用相应的编码方式。比如Windows 环境下通过代码页Code Page来区分具体编码将代码页设置为 936那么 ANSI 代表GBK简体中文代码页设置为 950ANSI 代表 Big5繁体中文代码页设置为 932ANSI 代表 Shift_JIS日文。可见代码页是具体字符编码的代号。ANSI 编码最常见的应用就是在 Windows 当中的记事本程序中当新建一个记事本默认的保存编码格式就是ANSI。不同 ANSI编码之间互不兼容当信息在国际间交流时就时常会出现令人头痛的乱码问题。要想查看 Windows 系统使用的代码页在命令行输入 chcp 命令后回车查看。5.中文编码本节讨论的内容主要围绕中文编码的发展以及各自编码之间的关系。计算机史上中国大陆以及台湾、香港等地区自行研发的中文编码方案主要有 GB2312GBKGB18030BIG5下面将一一讲解其大致的发展和特点。GB2312GB2312又名 GB2312–1980是中华人民共和国国家标准简体中文字符集全称《信息交换用汉字编码字符集》由中国国家标准总局1980年发布1981年5月1日实施。GB2312 通行于中国大陆新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化软件都支持 GB2312。GB2312 标准共收录6763个汉字其中一级汉字3755个二级汉字3008个。同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个字符。GB2312的出现基本满足了汉字的计算机处理需要它所收录的汉字已经覆盖中国大陆99.75%的使用频率。但对于人名、古汉语等方面出现的罕用字和繁体字GB2312不能处理因此后来GBK以及GB18030汉字字符集的相继出现解决了这些问题。GB2312 中如果一个字节是0127那么这个字节的含义同ASCII编码否则这个字节和下一个字节共同组成汉字或是GB编码定义的其他字符。所以GB2312对ASCII编码是兼容的。也就是说如果一段用GB2312编码的文本里所有字符都在ASCII中有定义那么这段编码和ASCII编码完全一样。GB13000、GBK、GB18030 的由来GB编码早期收录的汉字不足一万个基本满足日常使用需求但不包含一些生僻的字后来在一个个新版本中加进去。最早的GB编码是GB2312由于GB2312-80只收录6763个汉字根本不够用。1993年随着Unicode 1.1版本推出收录中国大陆、台湾、日本及韩国通用字符集的汉字总共有20,902个。同年我国按捺不住制定了等同于Unicode 1.1版本的国家中文编码标准GB13000全称GB13000.1-93采用双字节编码但因其与GB2312不兼容没有照顾到市场上软件厂商的感情因为大部分中文软件都是采用了GB2312所以一时间GB13000并没有得到广泛的应用现如今已是废弃的标准这也是我们很少听到这个编码标准的原因。GB13000 虽然没有得到应用但是收录了很多 GB2312 没有收录的汉字还是起到了一定的作用。如部分在GB2312-80推出以后才简化的汉字如“啰”部分人名用字如中国科学院院士林镕的“镕”字台湾及香港使用的繁体字日语及朝鲜语汉字等。参考 GB13000 收录的汉字微软利用 GB2312 未使用的编码空间与中国合作制订了 GBK。GBK 全称《汉字内码扩展规范》。GBK 即“国标扩展”汉语拼音的首字母英文名称为 Chinese Internal Code Specification。因为微软的介入GBK 只被中国有关部门作为技术规范并非国家正式标准只是国家技术监督局标准化司、电子工业部科技与质量监督司发布的“技术规范指导性文件”。虽然 GBK 收录了所有 Unicode 1.1 及 GB13000 之中的汉字但是编码方式与 Unicode 1.1 及 GB13000 不同。仅仅是 GB2312到 GB13000 之间的过渡方案。但因为其在 Windows95 简体中文版开始使用得到了广泛的推广成为了事实上不争的中文编码标准。GBK 是在GB2312标准基础上的内码扩展规范使用了双字节编码方案其编码范围从0x8140至0xFEFE排除部分码位共23940个码位共收录了21003个汉字和883个图形符号完全兼容GB2312支持国际标准ISO/IEC10646-1和国家标准GB13000收录的全部中日韩汉字并包含了 BIG5 编码中的所有汉字。GBK 编码方案于1995年10月制定 1995年12月正式发布目前中文版操作系统 Win95、Win98、Windows NT 以及 Windows 2000、Windows XP、Win7、Win8、Win10 等都支持 GBK 编码方案。最新的中文编码是 GB 18030国家质量技术监督局于2000年3月17日推出了 GB18030-2000 标准以取代 GBK加入了一些国内少数民族的文字一些生僻字被编到4个字节每扩展一次都完全保留之前版本的编码所以每个新版本都向下兼容。GB 18030 第二版为 GB 18030-2005为国家质量监督检验总局和中国国家标准化管理委员会于2005年11月8日发布2006年5月1日实施是在 GB 18030-2000 基础上增加了CJK统一汉字扩展 B 区的汉字。GB 18030-2005 共收录汉字 70,244 个。此标准内的单字节编码部分、双字节编码部分和四字节编码部分收录的少数中日韩统一表意文字扩展 A 区汉字为强制性标准。其他部分则属于规模性标准。在中华人民共和国境内所有软件产品都需要支持 GB 18030-2005。Big5 的由来20世纪80年代初期中国大陆制订了GB2312可能因为改革开放搞得热火朝天无暇顾及没有考虑到台湾香港澳门同胞的使用繁体中文的情况没有帮他们制定繁体中文编码主要原因是还没回归但是台湾同胞怎么能够容忍这种情况于是自己搞了个Big5繁体中文编码。Big5又称为大五码或五大码是使用繁体中文正体中文社区中最常用的电脑汉字字符集标准共收录13,060个汉字。Big5虽普及于台湾、香港与澳门等繁体中文通行区但长期以来并非当地的国家/地区标准或官方标准而只是业界标准。倚天中文系统、Windows繁体中文版等主要系统的字符集都是以Big5为基准但厂商又各自增加不同的造字与造字区派生成多种不同版本。2003年Big5被收录到CNS11643中文标准交换码的附录当中获取了较正式的地位。这个最新版本被称为Big5-2003。GBK、GB18030、Big5与ANSI的关系前面对ANSI已经有较详细的介绍讲到了GBKGB18030Big5这里再啰嗦一遍。除了中文本土的编码方案同样日文、韩文、世界各国文字都有它们各自的编码如果ASCII不能满足使用要求的话。这些编码都和GB编码相似兼容ASCII并用两个字节表示一个字。所有这些各国文字编码微软统称为ANSI 。所以即使知道是ANSI我们还需要知道这是哪国文字才能解码因为这些编码都互相冲突。另外你无法用一段ANSI编码表示既有汉字、又有韩字的文本。ANSI根据代码页来确定代表的具体编码例如简体中文GB2312的代码页是936。小结说了那么多简单的梳理一下中文相关编码之间的关系。从 ASCII、GB2312、GBK 到 GB18030这些编码方法是向下兼容的即同一个字符在这些方案中总是有相同的编码后面的标准支持更多的字符。在这些编码中英文和中文可以统一地处理。其中GBK 包含了 BIG5 编码中的所有汉字但是 GBK 不兼容 BIG5。GB13000 码值与 Unicode 中文字符码值相同与其它中文 GB 编码均不兼容。它们的关系如下图所示6.Unicode 与 BMP我们知道英语中的 128 字符使用单字节 7bits 的 ASCII 编码就够了但是用来表示其他语言128 个码位号是远远不够的。比如法语字母上方有注音符号它就无法用 ASCII 码表示。于是一些欧洲国家就决定利用字节中闲置的最高位编入新的符号。比如法语中的é的编码为130二进制 10000010。这样一来这些欧洲国家使用编码 Latin1就可以最多表示 256 个符号。但是对于亚洲国家的文字使用的符号就更多了汉字就多达10万左右。单字节编码方案最多只能表示256种符号肯定是不够的就必须使用多个字节表达一个符号。比如简体中文常见的编码方式GB2312使用两个字节表示一个汉字所以理论上最多可以表示256x25665536个符号。这里不详细展开后面会具体讨论GB2312。每个国家或地区都有自己的一套编码方案于是当信息在国际间间流是就会出现乱码问题好比世界上每个国家都有自己的语言相互交流时就会出现障碍。于是就需要一个国际语言让每个国家和地区的人之间可以正常的交流对于计算机也是同样的道理需要一个统一的字符编码方案让每一台电脑都能正确的识别字符。铺垫了那么多就是想说明一个叫Unicode的字符编码横空出世的必要性和意义。Unicode 俗称万国码是由统一码联盟在1991年首次发布请注意并非由ISO发布。它对世界上大部分的文字系统进行了整理、编码使得电脑可以跨语言环境来呈现和处理文字。需要注意的是Unicode虽然称为万国码但是目前也不能涵盖世界上所有的文字字符因为Unicode自发布以来至今仍在不断增修每个新版本都加入更多新的字符。目前最新的版本为2016年6月21日公布的Unicode9.0.0已经收入超过十万个字符我们华夏民族的字符也不止十万个啊Uinicode仍需努力啊。Unicode 编码方式。Unicode 的编码空间从 U0000 到 U10FFFF共有 1,112,064 个码位code point可用来映射字符。Unicode 的编码空间可以划分为 17 个平面plane每个平面包含2 16 2^{16}21665,536个码位。17 个平面的码位可表示为从 Uxx0000 到 UxxFFFF其中 xx 表示十六进制值从00 16 00_{16}0016​到10 16 10_{16}1016​共计 17 个平面。第一个平面称为基本多语言平面Basic Multilingual Plane, BMP或称零号平面Plane 0。其他平面称为辅助平面Supplementary Planes。基本多语言平面内从 UD800 到 UDFFF 之间的码位区块是永久保留不映射到 Unicode 字符。实际使用中目前只用了少数平面内编码的字符。平面始末字符值中文名称英文名称0号平面U0000 - UFFFF基本多文种平面Basic Multilingual Plane简称BMP1号平面U10000 - U1FFFF多文种补充平面Supplementary Multilingual Plane简称SMP2号平面U20000 - U2FFFF表意文字补充平面Supplementary Ideographic Plane简称SIP3号平面U30000 - U3FFFF表意文字第三平面未正式使用[1]Tertiary Ideographic Plane简称TIP4号平面~13号平面U40000 - UDFFFF尚未使用14号平面UE0000 - UEFFFF特别用途补充平面Supplementary Special-purpose Plane简称SSP15号平面UF0000 - UFFFFF保留作为私人使用区A区Private Use Area-A简称PUA-A16号平面U100000 - U10FFFF保留作为私人使用区B区Private Use Area-B简称PUA-B其中中国由 GB2312 编码表示的常用的 6763 个汉字就被收录在 Unicode 的零号平面内 U4E00-U9FFF 码值之间该区间的码值也包含也很多非常用的中文汉字共收录了 2W 多个汉字。Unicode 对各国语言文字的编码情况具体可参见维基百科.Unicode字符平面映射。7.UCS、UCS-2 与 UCS-4说到字符编码大家肯定听过UCS-2和UCS-4在说完Unicode好学的大家肯定心存疑惑UCS-2和UCS-4和Unicode之间的关系和区别到底是什么我曾经也为此痛苦不已但是下面我将努力尝试捋清楚UCS-2与Unicode之间千丝万缕的关系为大家答疑解惑。首先说一下什么是UCS。UCSUniversal Character Set通用字符集是由ISO制定的ISO 10646或称ISO/IEC 10646标准所定义的标准字符集。UCS又被称为Universal Multiple-Octet Coded Character Set中国大陆译为通用多八位编码字符集台湾译为广用多八比特编码字元集。说到UCS不得不说UCS和Unicode的关系。历史上存在两个独立的尝试创立单一字符集的组织即国际标准化组织ISO于1984年创建的ISO/IEC JTC1/SC2/WG2英文全称International Organization for Standardization / International Electrotechnical Commission, Joint Technical Committee#1/Subcommittee#2/Working Group#2和由Xerox、Apple等软件制造商于1988年组成的统一码联盟。前者开发了ISO/IEC 10646UCS项目后者开发了统一码Unicode项目。因此最初制定了不同的标准。1991年前后两个项目的参与者都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果并为创立一个单一编码表而协同工作。1991年不包含CJK统一汉字集的Unicode 1.0发布。随后CJK统一汉字集的制定于1993年完成发布了ISO 10646-1:1993即Unicode 1.1。从Unicode 2.0开始Unicode采用了与ISO 10646-1相同的字库和字码。ISO也承诺ISO 10646将不会替超出U10FFFF的UCS-4编码赋值以使得两者保持一致。两个项目仍都独立存在并独立地公布各自的标准。但统一码联盟和ISO/IEC JTC1/SC2都同意保持两者标准的码表兼容并紧密地共同调整任何未来的扩展。也就是说我们可以简单的理解Unicode和UCS是两个不同机构发布的对全球文字字符进行统一编码的相同方案更为简单粗暴的理解就是“UnicodeUCS”。UCS 与 Unicode 的区别。UCS和Unicode毕竟是两个不同机构研发的编码方案它们之间还是存在着一些区别。Unicode和UCS虽然对全球字符编码的码值相同ISO/IEC 10646标准就像ISO/IEC 8859标准一样只不过是一个简单的字符集表但Unicode标准额外定义了许多与字符有关的语义符号学。Unicode详细说明了绘制某些语言如阿拉伯语表达形式的算法处理双向文字比如拉丁文和希伯来文的混合文字的算法排序与字符串比较所需的算法等等。此外两者部分样例字形有显著的区别。ISO/IEC 10646-1标准同样使用四种不同的风格变体来显示表意文字如中文、日文、韩文即CJK但Unicode 2.0的表里只有中文的变体。甚至存在“Unicode对日本用户来说不可接受”的不实传说。UCS 是 ISO 研发的全球通用字符集那么 UCS-2 又是什么呢UCS-22-byte Universal Character Set两字节通用字符集是一个实际使用的字符编码方案是UTF-16的前身。还记得前面说到的Unicode的BMP吗就是Unicode使用两字节来编码全球大部分文字字符的一个编码区间号称0号平面UCS-2是一个固定两字节长度的编码每一个字符都采用一个单一的16位值来表示因此只能表示Unicode的BMP范围的码值从U0000到UFFFF的字符。那么UCS-2和Unicode的0好平面又是啥关系呢其实UCS-2编码的字符和Unicode的BMP编码的字符是相同的因此UCS-2就是Unicode的BMP。那么UCS-2是那个机构颁发的呢很显然是ISO。那么UCS-2和UCS有时什么关系呢UCS-2是UCS的子集UCS-2是UCS的编码方式之一。其中中文范围 4E00-9FBF即CJK 统一表意符号 (CJK Unified Ideographs)。UCS-4 又是什么呢UCS-2采用两个字节编码字符只能标识65536个字符对于Unicode编码的字符已经超过了十万个很显然UCS-2只能标识了Unicode的0号平面字符对于其它辅助平面字符UCS-2就无能为力心有余而力不足了。于是ISO 10646标准定义了一个4字节31位的编码形式称作UCS-4来标识的Unicode其它辅助平面编码的字符。UCS-4对所有的字符均采用四字节31位编码形式码值范围是0x00000000-0x7FFFFFFF。简短总结。UCS-2Unicode BMPUnicode是UCS-4的子集。8.UTF-8、UTF-16与UTF-328.1 UTF-8Unicode 编码系统可分为编码方式和实现方式两个层次。上面关于Unicode编码系统的解释主要叙述了其的编码方式即 Uinicode 每一个字符赋予了确切的不同的码值但是实际使用当中其实现方式是不同于编码方式的。一个字符的Unicode编码是确定的但是在实际传输过程中由于不同系统平台的设计不一定一致以及出于节省空间的目的对Unicode编码的实现方式有所不同这就是为何已经存在了UCS-2和UCS-4仍提出UTF-8、UTF-16和UTF-32。Unicode的实现方式称为Unicode转换格式Unicode Transformation FormatUTF。UTF-88-bit Unicode Transformation Format是一种针对Unicode的可变长度字符编码也是一种前缀码由肯·汤普逊Ken Thompson于1992年创建现在已经标准化为RFC 3629。它可以用来表示Unicode标准中的任何字符且其编码中的第一个字节仍与ASCII兼容这使得原来处理ASCII字符的软件无须或只须做少部分修改即可继续使用。因此它逐渐成为电子邮件、网页及其他存储或发送文字的应用中优先采用的编码。UTF-8 编码方式UTF-8 就是以 8 位为单元对UCS进行编码而UTF-8不使用大尾序大端字节序和小尾序小端字节序的形式每个使用UTF-8存储的字符除了第一个字节外其余字节的头两个比特都是以10开始使文字处理器能够较快地找出每个字符的开始位置。但为了与以前的 ASCII 码兼容ASCII 为一个字节因此 UTF-8 选择了使用可变长度字节来存储 UnicodeUnicode 和 UTF-8 之间的转换关系表 ( x 字符表示码点占据的位 )。码点的位数码点起值码点终值字节序列Byte 1Byte 2Byte 3Byte 4Byte 5Byte 67U0000U007F10xxxxxxx11U0080U07FF2110xxxxx10xxxxxx16U0800UFFFF31110xxxx10xxxxxx10xxxxxx21U10000U1FFFFF411110xxx10xxxxxx10xxxxxx10xxxxxx26U200000U3FFFFFF5111110xx10xxxxxx10xxxxxx10xxxxxx10xxxxxx31U4000000U7FFFFFFF61111110x10xxxxxx10xxxxxx10xxxxxx10xxxxxx10xxxxxx必须要注意的是2003年11月UTF-8 被 RFC 3629 限制了长度因为 Unicode 码值 0x000000-0x10FFFF只有21位被编码所以使用 4 个字节的就可以编码现有的 Unicode 字符所以 UTF-8 被缩减为四字节码值由原来的 1~6 字节缩减为 1-4 字节新的 UTF-8 编码方式与 Unicode 码值的对应关系如下字节数码点位数码点起值码点终值Byte 1Byte 2Byte 3Byte 417U0000U007F0xxxxxxx211U0080U07FF110xxxxx10xxxxxx316U0800UFFFF1110xxxx10xxxxxx10xxxxxx421U10000U10FFFF11110xxx10xxxxxx10xxxxxx10xxxxxx已知“严”的 Unicode 是 4E2501001110 00100101根据上表可以发现 4E25 处在第三行的范围内U0800-UFFFF因此“严”的UTF-8编码需要三个字节即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后从“严”的最后一个二进制位开始依次从后向前填入格式中的x多出的位补0。这样就得到了“严”的UTF-8编码是“11100100 10111000 10100101”转换成十六进制就是E4B8A5。我们以Notepad需要安装插件HEX-Editor安装方法查看中文严字的UTF-8编码。8.2 UTF-16UTF-16 类似于 UTF-8都是变长字符编码都是 Unicode 的实现方式之一。UTF-16 与 UTF-8 的主要区别是 UTF-16 最短编码长度是2个字节UTF-8 是1个字节。还有就是 UTF-8 不存在字节序的问题UTF-16存在字节序的问题。与此同时UTF-16还利用了Unicode保留下来的0xD800-0xDFFF区段的码位来对辅助平面的字符的码位进行编码。大家可能会有疑问有了应用广泛的 UTF-8为何还要搞个 UTF-16 呢还记得前面因为 UCS-2 的双字节码位不够IEEE 提出的 UCS-4 编码吗因为 UCS-4 规定了每一个字符需要4个字节31bits来表示这样太浪费存储空间了为了解决这个问题于是国际互联网工程任务组The Internet Engineering Task ForceIETF于 2000 年提出了 UTF-16 编码方案并发表在 RFC 2781。UTF-16编码方式。UTF16 使用 1 个或 2 个 16 位长的码元来表示是一个变长编码方案实现方式如下码值范围10进制码值范围字节数UTF-16编码U0000-UFFFF0-655352xxxxxxxxxxxxxxxxU10000-U10FFFF65536-11141114110110yyyyyyyyyy 110111xxxxxxxxxx对于码值从U0000至UFFFF的零号平面字符UTF-16编码的码值与Unicode码值相同。对于码值从U10000至U10FFFF的辅助平面字符UTF-16编码方式将Unicode码值减去0x10000将码值范围变成0x00000-0FFFFF填入上面表格第二行的20 bits中。从左至右两个码元的取值范围分别是0xD800-0xDBFF和0xDC00-0xDFFF。由于码值0xD800-0xDFFF在Unicode零号平面内不表示任何字符故在UTF-16中用作代理来表示码值从U10000至U10FFFF的辅助平面字符。UTF-16比起UTF-8好处在于大部分字符都以固定长度的字节2字节存储但UTF-16却无法兼容于ASCII编码因UTF-8兼容 ASCII能适应许多 C 库中的 \0’结尾惯例没有字节序问题以及英文和西文符号比较多的场景下如 HTML/XML编码较短的优点UTF-8 编码比 UTF-16 编码应用更为广泛。UTF-16和UCS-2的区别与联系。第一个Unicode平面码位从U0000至UFFFF包含了最常用的字符。UTF-16与UCS-2编码这个范围内的码位需要16比特长的单个码元数值等价于对应的码位。BMP中的这些码位是仅有的可以在UCS-2中表示的码位。但是对于BMP外的其它辅助平面字符UCS-2却无法表示但是UTF-16用1个或者2个16位长的码元来表示既可以容纳UCS-2也可以表示辅助平面字符因此UTF-16可看成是UCS-2的父集。在没有辅助平面字符前UTF-16与UCS-2所指的是同一的意思。但当引入辅助平面字符后就称为UTF-16了。现在若有软件声称自己支持UCS-2编码那其实是暗指它不能支持在UTF-16中超过2字节的字集。8.3 UTF-32UTF-32是采用定长4字节来表示Unicode字符不同于其它的Unicode转换格式UTF-8和UTF-16则使用不定长度编码。UTF-32在实际应用中也很少被使用因为UTF-32对每个字符都使用4字节就空间而言是非常低效的。特别地非基本多文种平面的字符在大部分文件中通常很罕见以致于它们通常被认为不存在占用空间大小的讨论使得UTF-32通常会是其它编码的二到四倍。UTF-32与UCS-4关系。通用字符集UCS的每一个字符会在0到0x7FFFFFFF这样的字码空间中被表示成一个的31位的码值。UCS-4足以用来表示所有的Unicode的字码空间最大码位为0x7FFFFFFF其空间约为20亿个码位。有些人认为保留如此大的字码空间却只为了对应很小的码集是很浪费的所以一个新的编码UTF-32被提出来了。UTF-32是一个UCS-4 的子集使用4字节对字符编码但是码值只从0到0x10FFFF百万个码位与Unicode码值一一对应。UTF-32 原本是 UCS-4 的子集但JTC1/SC2/WG2声明未来所有对字符的指定都将会限制在BMP及其14个补充平面。9.Little Endian、Big Endian与BOM字节序与大小端是伴随着多字节字符集Multibyte Character SetMBCS而出现的问题。单字节编码如ASCII是不存在编码字节序问题的每一个字节代表一个字符但是对于Unicode多字节字符编码如 UTF-16 和 UTF-32就会存在字节序的问题。例如“奎”的Unicode编码是594E“乙”的 Unicode 编码是 4E59。如果我们收到 UTF-16 字节流 594E那么这是“奎”还是“乙”编码存储差异。这里就要引出两个名词LELittle Endian与 BEBig Endian。LELittle Endian)小端字节序意思就是一个单元在计算机中的存放时按照低位在低地址高位在高地址的模式存放BEBig Endian大端字节序和LE相反是高位在低地址低位在高地址的模式存放。例如“汉”字的 Unicode 编码是 U6C49。那么写到文件里时究竟是将6C写在前面还是将49写在前面如果将6C写在前面就是Big Endian。如果将 49 写在前面就是 Little Endian。Endian 一词出自《格列佛游记》小人国的内战就源于吃鸡蛋时究竟从大头Big Endian敲开还是从小头Little Endian敲开由此曾发生过六次叛乱一个皇帝送了命另一个丢了王位。我们一般将 Endian 翻译成字节序将 Big Endian 和 Little Endian 称作大尾序和小尾序或者大端序和小端序。编码存储差异解决办法BOM。为了解决上面存储时字节序的问题Unicode规范中推荐的标记字节顺序的方法是BOMByte Order Mark头意思是字节序标志头。在UCS编码中有一个叫做零宽度非换行空格ZERO WIDTH NO-BREAK SPACE的字符它的编码是UFEFF。而UFEFF在UCS中是不存在的字符所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前先传输字符零宽度非换行空格字符。这样如果接收者收到FEFF就表明这个字节流是Big-Endian的如果收到FFFE就表明这个字节流是Little-Endian的。因此字符ZERO WIDTH NO-BREAK SPACE又被称作BOM。UTF-8 不需要 BOM 来表明字节顺序但可以用 BOM 来表明编码方式。UTF-8 编码的 BOM 是 EF BB BF。所以如果接收者收到以 EF BB BF 开头的字节流就知道这是 UTF-8 编码了。Windows 就是使用 BOM 来标记文本文件的编码方式的。通过它基本能确定编码格式和字节序。UTF 相关编码的 BOM 如下。UTF编码BOMUTF-8EF BB BFUTF-16 LEFF FEUTF-16 BEFE FFUTF-32 LEFF FE 00 00UTF-32 BE00 00 FE FF记事本联通乱码。如果没有 BOM 只能靠猜了。软件读入文件时可以所有编码都试一下看哪个像。另外BOM 只针对Unicode系列编码ANSI通通不使用 BOM。很显然没有BOM难免偶然猜错。网上就流传着一个神奇的段子在 Windows XP 或 Win7 下打开记事本输入“联通”二字保存关闭再打开变成了个黑块。注该 bug 在 Win10 实测已修复有人说这是因为联通得罪了微软当然这是玩笑话。ANSI是不带BOM的所以联通的ANSI是GBK编码码值为C1 AA CD A8 码值的查看可以通过Notepad的插件Hex-Editor十六进制编辑器来显示如下“联通”码值对应的二进制码值如下C1 110 00001 AA 10 101010 CD 110 01101 A8 10 101000第一二字节和第三四个字节的起始部分的都是110和10正好与UTF8规则里的两字节模板是一致的于是再次打开记事本时记事本误认为这是一个UTF8编码的文件把第一个字节110和第二个字节的10去掉我们就得到了00001 101010再把各位对齐补上前导的0就得到了0000 0000 0110 1010Unicode码值为U006A这是小写的字母“j”由于字母j理应按照ASCII字符编码在UTF-8中以单字节进行编码所以解析失败显示两个乱码。而之后的两字节用UTF8解码之后是U0368查看Unicode字符表发现为字符COMBINING LATIN SMALL LETTER C显示为极小的字母c。以上原因导致了“联通”两个字没有办法在记事本里正常显示。可以认为GBK文档中的所有字符的二进制编码第一个字节在[0xC0, 0xDF]并且第二字节在[0x80,0xBF]区间时记事本都无法确认文本的编码格式就会按照UTF-8的格式来显示比如“透支”二字也会出现乱码。BOM听起来很不错但实际是个讨厌的设计因为它和很多协议、规范不兼容这是题外话。有了BOM于是很多文本编辑软件就会有UTF-8 without BOMUTF-16 without BOM编码格式的选项。如果不提BOM究竟有BOM还是没有BOM又是一个十分纠结的问题Windows里的软件一般都默认有BOM而其它系统都默认没有BOM可能是因为Windows常要兼容ANSI的原因特别依赖BOM来防止出错。10.内码、外码与代码页Code Page前面在描述相关字符编码时也涉及到内码和代码页但没有详细展开这里简要的说明一下。内码与外码关系。内码是指操作系统内部的字符编码内码其实就是字符编码。之所以称之为内码是因为有外码这种东西。汉字输入码外码是指用户从键盘上键入汉字时所使用的汉字编码计算机内部存储的就是汉字的内码。常用的输入码有数字编码-区位码拼音编码-全拼、双拼、微软拼音输入法、自然码、智能ABC、搜狗等等字形编码-五笔、表形码、郑码输入法等。早期操作系统的内码是与语言相关的现在的Windows在内部统一使用Unicode然后用代码页适应各种语言内码的概念就比较模糊了。我们一般将缺省代码页指定的编码说成是内码。内码这个词汇并没有什么官方的定义。代码页也只是微软的一种习惯叫法。作为程序 员我们只要知道它们是什么东西没有必要过多地考证这些名词。代码页是什么目前Windows的内核已经支持Unicode字符集这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特定语言的编码例如GBKWindows不可能不支持现有的编码而全部改用Unicode。于是Windows使用代码页(code page)来标识各个国家和地区字符编码所以代码页就是字符编码的代号。例如Windows系统中GB2312对应的code page是CP20936BIG5的code page是CP950GBK对应的code page是CP936。GB18030对应的code pageCP54936。11.后记这篇杂谈的所谈论的内容实在是太庞杂了坚持了四天终于初步完成了本篇博文。字符编码涉及的用语和概念繁多仔细考究的话没有几十本著作和文献应该是没法说清楚的所以本篇短短1W多字的精炼描述是不可能对字符编码的历史编码方式和关系完美诠释的只求让大家有个大致的了解免受因对字符编码的不解而带来的痛苦和困惑。文章操之过急参考文献不足再加上本人水平有限难免出现不足和错误之处望大家批评指正留言探讨。这应该是我在腾讯实习的最后一篇 blog了离开前留下点东西刻下我在鹅场三个多月实习的痕迹。参考文献[1] 为什么计算机能读懂1和0[2] latin1.百度百科[3] ISO-8859-1.维基百科[4] ANSI编码.百度百科[5] Unicode.维基百科[6] Unicode字符平面映射.维基百科[7] 谈谈Unicode编码简要解释UCS、UTF、BMP、BOM等名词[8] 通用字符集.维基百科[9] UTF-16/UCS-2.维基百科[10] UTF-32.wikipedia[11] UTF-8.维基百科[12] UTF-8.wikipedia[13] UTF-16.维基百科[14] UTF-16.wikipedia[15] UTF-32.wikipedia[16] 遇到乱码不怕不怕啦——计算机字符详尽讲解[17] GB2312.维基百科[18] 汉字内码扩展规范.维基百科[19] 彻底搞懂字符编码(unicode,mbcs,utf-8,utf-16,utf-32,big endian,little endian…)[20] windows内码、外码、字符映射表
返回列表