UTF-8

摘自:https://zh.wikipedia.org/wiki/UTF-8,个别地方有几个特殊符号没被处理,显示成层叠状态。php

维基百科,自由的百科全书

UTF-88-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,也是一种前缀码。它能够用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须作少部分修改,便可继续使用。所以,它逐渐成为电子邮件网页及其余存储或发送文字的应用中,优先采用的编码。html

UTF-8使用一至六个字节为每一个字符编码(尽管如此,2003年11月UTF-8被RFC 3629从新规范,只能使用原来Unicode定义的区域,U+0000到U+10FFFF,也就是说最多四个字节):java

  1. 128个US-ASCII字符只需一个字节编码(Unicode范围由U+0000至U+007F)。
  2. 带有附加符号拉丁文希腊文西里尔字母亚美尼亚语希伯来文阿拉伯文叙利亚文它拿字母则须要两个字节编码(Unicode范围由U+0080至U+07FF)。
  3. 其余基本多文种平面(BMP)中的字符(这包含了大部分经常使用字,如大部分的汉字)使用三个字节编码(Unicode范围由U+0800至U+FFFF)。
  4. 其余极少使用的Unicode 辅助平面的字符使用四至六字节编码(Unicode范围由U+10000至U+1FFFFF使用四字节,Unicode范围由U+200000至U+3FFFFFF使用五字节,Unicode范围由U+4000000至U+7FFFFFFF使用六字节)。

对上述说起的第四种字符而言,UTF-8使用四至六个字节来编码彷佛太耗费资源了。但UTF-8对全部经常使用的字符均可以用三个字节表示,并且它的另外一种选择,UTF-16编码,对前述的第四种字符一样须要四个字节来编码,因此要决定UTF-8或UTF-16哪一种编码比较有效率,还要视所使用的字符的分布范围而定。不过,若是使用一些传统的压缩系统,好比DEFLATE,则这些不一样编码系统间的的差别就变得微不足道了。若顾及传统压缩算法在压缩较短文字上的效果不大,能够考虑使用Unicode标准压缩格式(SCSU)。linux

互联网工程工做小组(IETF)要求全部互联网协议都必须支持UTF-8编码[1]互联网邮件联盟(IMC)建议全部电子邮件软件都支持UTF-8编码。[1]正则表达式

历史[编辑]

1992年初,为建立良好的字节串编码系统以供多字节字符集使用,开始了一个正式的研究。ISO/IEC 10646的初稿中有一个非必须的附录,名为UTF。当中包含了一个供32比特字符使用的字节串编码系统。这个编码方式的性能并不使人满意,但它提出了将0-127的范围保留给ASCII以兼容旧系统的概念。算法

1992年7月,X/Open委员会XoJIG开始寻求一个较佳的编码系统。Unix系统实验室(USL)的Dave Prosser为此提出了一个编码系统的建议。它具有可更快速实现的特性,并引入一项新的改进。其中,7比特ASCII符号只表明原来的意思,全部多字节序列则会包含第8比特的符号,也就是所谓的最高有效比特数据库

1992年8月,这个建议由IBMX/Open的表明流传到一些感兴趣的团体。与此同时,贝尔实验室九号项目操做系统工做小组的肯·汤普逊对这编码系统做出重大的修改,让编码能够自我同步,使得没必要从字符串的开首读取,也能找出字符间的分界。1992年9月2日,肯·汤普逊罗勃·派克一块儿在美国新泽西州一架餐车的餐桌垫上描绘出此设计的要点。接下来的日子,Pike及汤普逊将它实现,并将这编码系统彻底应用在九号项目当中,及后他将有关成果回馈X/Open。api

1993年1月25-29日的在圣地牙哥举行的USENIX会议首次正式介绍UTF-8。浏览器

自1996年起,微软CAB(MS Cabinet)规格在UTF-8标准正式落实前就明确允许在任何地方使用UTF-8编码系统。但有关的编码器实际上历来没有实现这方面的规格。网络

描述[编辑]

Unicode与UTF-8的转换

目前有好几份关于UTF-8详细规格的文件,但这些文件在定义上有些许的不一样:

  • RFC 3629 / STD 63(2003),这份文件制定了UTF-8是标准的互联网协议元素
  • 第四版,The Unicode Standard,§3.9-§3.10(2003)
  • ISO/IEC 10646-1:2000附加文件D(2000)

它们替换了如下那些被淘汰的定义:

  • ISO/IEC 10646-1:1993修正案2/附加文件R(1996)
  • 第二版,The Unicode Standard,附录A(1996)
  • RFC 2044(1996)
  • RFC 2279(1998)
  • 第三版,The Unicode Standard,§2.3(2000)及勘误表#1:UTF-8 Shortest Form(2000)
  • Unicode Standard附加文件#27: Unicode 3.1(2001)

事实上,全部定义的基本原理都是相同的,它们之间最主要的不一样是支持的字符范围及无效输入的处理方法。

Unicode字符比特被分区为数个部分,并分配到UTF-8的字节串中较低的比特的位置。在U+0080的如下字符都使用内含其字符的单字节编码。这些编码正好对应7比特的ASCII字符。在其余状况,有可能须要多达4个字符组来表示一个字符。这些多字节的最高有效比特会设置成1,以防止与7比特的ASCII字符混淆,并保持标准的字节主导字符串运做顺利。

代码范围
十六进制
标量值(scalar value)
二进制
UTF-8
二进制十六进制
注释
000000 - 00007F
128个代码
00000000 00000000 0zzzzzzz 0zzzzzzz(00-7F) ASCII字符范围,字节由零开始
七个z 七个z
000080 - 0007FF
1920个代码
00000000 00000yyy yyzzzzzz 110yyyyy(C0-DF) 10zzzzzz(80-BF) 第一个字节由110开始,接着的字节由10开始
三个y;二个y;六个z 五个y;六个z
000800 - 00D7FF
00E000 - 00FFFF
61440个代码 [Note 1]
00000000 xxxxyyyy yyzzzzzz 1110xxxx(E0-EF) 10yyyyyy 10zzzzzz 第一个字节由1110开始,接着的字节由10开始
四个x;四个y;二个y;六个z 四个x;六个y;六个z
010000 - 10FFFF
1048576个代码
000wwwxx xxxxyyyy yyzzzzzz 11110www(F0-F7) 10xxxxxx 10yyyyyy 10zzzzzz 将由11110开始,接着的字节由10开始
三个w;二个x;四个x;四个y;二个y;六个z 三个w;六个x;六个y;六个z
Note 1 Unicode在范围D800-DFFF中不存在任何字符, 基本多文种平面中约定了这个范围用于UTF-16扩展标识 辅助平面(两个UTF-16表示一个 辅助平面字符)。固然,任何编码都是能够被转换到这个范围,但在unicode中他们并不表明任何合法的值。

例如,希伯来语字母aleph(א)的Unicode代码是U+05D0,按照如下方法改为UTF-8:

  • 它属于U+0080到U+07FF区域,这个表说明它使用双字节,110yyyyy 10zzzzzz.
  • 十六进制的0x05D0换算成二进制就是101-1101-0000.
  • 这11位数按顺序放入"y"部分和"z"部分:11010111 10010000.
  • 最后结果就是双字节,用十六进制写起来就是0xD7 0x90,这就是这个字符aleph(א)的UTF-8编码。

因此开始的128个字符(US-ASCII)只需一字节,接下来的1920个字符须要双字节编码,包括带附加符号拉丁字母希腊字母西里尔字母科普特语字母,亚美尼亚语字母,希伯来文字母和阿拉伯字母的字符。基本多文种平面中其他的字符使用三个字节,剩余字符使用四个字节。

根据这种方式能够处理更大数量的字符。原来的规范容许长达6字节的序列,能够覆盖到31位(通用字符集原来的极限)。尽管如此,2003年11月UTF-8被RFC 3629从新规范,只能使用原来Unicode定义的区域,U+0000到U+10FFFF。根据这些规范,如下字节值将没法出如今合法UTF-8序列中:

编码(二进制 编码(十六进制 注释
1100000x C0, C1 过长编码:双字节序列的头字节,但码点 <= 127
1111111x FE, FF 没法达到:7或8字节序列的头字节
111110xx
1111110x
F8, F9, FA, FB, FC, FD 被RFC 3629规范:5或6字节序列的头字节
11110101
1111011x
F5, F6, F7 被RFC 3629规范:码点超过10FFFF的头字节

UTF-8编码字节含义[编辑]

  • 对于UTF-8编码中的任意字节B,若是B的第一位为0,则B独立的表示一个字符(ASCII码);
  • 若是B的第一位为1,第二位为0,则B为一个多字节字符中的一个字节(非ASCII字符);
  • 若是B的前两位为1,第三位为0,则B为两个字节表示的字符中的第一个字节;
  • 若是B的前三位为1,第四位为0,则B为三个字节表示的字符中的第一个字节;
  • 若是B的前四位为1,第五位为0,则B为四个字节表示的字符中的第一个字节;

所以,对UTF-8编码中的任意字节,根据第一位,可判断是否为ASCII字符;根据前二位,可判断该字节是否为一个字符编码的第一个字节;根据前四位(若是前两位均为1),可肯定该字节为字符编码的第一个字节,而且可判断对应的字符由几个字节表示;根据前五位(若是前四位为1),可判断编码是否有错误或数据传输过程当中是否有错误。

设计UTF-8的理由[编辑]

UTF-8的设计有如下的多字符组序列的特质:

  • 单字节字符的最高有效比特永远为0。
  • 多字节序列中的首个字符组的几个最高有效比特决定了序列的长度。最高有效位为110的是2字节序列,而1110的是三字节序列,如此类推。
  • 多字节序列中其他的字节中的首两个最高有效比特为10

UTF-8的这些特质,保证了一个字符的字节序列不会包含在另外一个字符的字节序列中。这确保了以字节为基础的部分字符串比对(sub-string match)方法能够适用于在文字中搜索字或词。有些比较旧的可变长度8位编码(如Shift JIS)没有这个特质,故字符串比对的算法变得至关复杂。虽然这增长了UTF-8编码的字符串的信息冗余,可是利多于弊。另外,数据压缩并不是Unicode的目的,因此不可混为一谈。即便在发送过程当中有部分字节因错误或干扰而彻底丢失,仍是有可能在下一个字符的起点从新同步,令受损范围受到限制。

另外一方面,因为其字节序列设计,若是一个疑似为字符串的序列被验证为UTF-8编码,那么咱们能够有把握地说它是UTF-8字符串。一段两字节随机序列碰巧为合法的UTF-8而非ASCII的几率为32分1。对于三字节序列的几率为256分1,对更长的序列的几率就更低了。

UTF-8的编码方式[编辑]

UTF-8是UNICODE的一种变长度的编码表达方式《通常UNICODE为双位元组(指UCS2)》,它由肯·汤普逊Ken Thompson于1992年建立,如今已经标准化为RFC 3629。UTF-8就是以8位为单元对UCS进行编码,而UTF-8不使用大尾序和小尾序的形式,每一个使用UTF-8存储的字符,除了第一个字节外,其他字节的头两个比特都是以"10"开始,使文字处理器可以较快地找出每一个字符的开始位置。

但为了与之前的ASCII码兼容(ASCII为一个字节),所以UTF-8选择了使用可变长度字节来存储Unicode:

Unicode 和 UTF-8 之间的转换关系表 ( x 字符表示码点占据的位 )
码点的位数 码点起值 码点终值 字节序列 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6
  7 U+0000 U+007F 1 0xxxxxxx
11 U+0080 U+07FF 2 110xxxxx 10xxxxxx
16 U+0800 U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
21 U+10000 U+1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
26 U+200000 U+3FFFFFF 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
31 U+4000000 U+7FFFFFFF 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  • 在ASCII码的范围,用一个字节表示,超出ASCII码的范围就用字节表示,这就造成了咱们上面看到的UTF-8的表示方法,这様的好处是当UNICODE文件中只有ASCII码时,存储的文件都为一个字节,因此就是普通的ASCII文件无异,读取的时候也是如此,因此能与之前的ASCII文件兼容。
  • 大于ASCII码的,就会由上面的第一字节的前几位表示该unicode字符的长度,好比110xxxxx前三位的二进制表示告诉咱们这是个2BYTE的UNICODE字符;1110xxxx是个三位的UNICODE字符,依此类推;xxx的位置由字符编码数的二进制表示的位填入。越靠右的x具备越少的特殊意义。只用最短的那个足够表达一个字符编码数的多字节串。注意在多字节串中,第一个字节的开头"1"的数目就是整个串中字节的数目。

ASCII字母继续使用1字节存储,重音文字希腊字母西里尔字母等使用2字节来存储,而经常使用的汉字就要使用3字节。辅助平面字符则使用4字节。

在UTF-8文件的开首,不少时都放置一个U+FEFF字符(UTF-8以EF,BB,BF表明),以显示这个文本文件是以UTF-8编码。

UTF-8的特性[编辑]

UTF-8图表说明
  UTF-8
最小码位 0000
最大码位 10FFFF
每字节所占位数 8 bits
Byte order N/A
每一个字符最小字节数 1
每一个字符最大字节数 4
  • UCS字符U+0000到U+007F(ASCII)被编码为字节0x00到0x7F(ASCII兼容),这也意味着只包含7位ASCII字符的文件在ASCII和UTF-8两种编码方式下是同样的。
  • 全部>U+007F的UCS字符被编码为一个多个字节的串,每一个字节都有标记位集。所以,ASCII字节(0x00-0x7F)不可能做为任何其余字符的一部分。
  • 表示非ASCII字符的多字节串的第一个字节老是在0xC0到0xFD的范围里,并指出这个字符包含多少个字节。多字节串的其他字节都在0x80到0xBF范围里,这使得从新同步很是容易,并使编码无国界,且不多受丢失字节的影响。
  • 能够编入全部可能的231个UCS代码
  • UTF-8编码字符理论上能够最多到6个字节长,然而16位BMP字符最多只用到3字节长。
  • Bigendian UCS-4字节串的排列顺序是预约的。
  • 字节0xFE和0xFF在UTF-8编码中从未用到,同时,UTF-8以字节为编码单元,它的字节顺序在全部系统中都是一様的,没有字节序的问题,也所以它实际上并不须要BOM
  • 与UTF-16或其余Unicode编码相比,对于不支持Unicode和XML的系统,UTF-8更不容易形成问题。

UTF-8编码的优势[编辑]

整体来讲,在Unicode字符串中不可能由码点数量决定显示它所须要的长度,或者显示字符串以后在文本缓冲区中光标应该放置的位置;组合字符、变宽字体、不可打印字符和从右至左的文字都是其归因。

因此尽管在UTF-8字符串中字符数量与码点数量的关系比UTF-32更为复杂,在实际中不多会遇到有不一样的情形。

更详细的说,UTF-8编码具备如下几点优势:

  • ASCII是UTF-8的一个子集。由于一个纯ASCII字符串也是一个合法的UTF-8字符串,因此现存的ASCII文本不须要转换。为传统的扩展ASCII字符集设计的软件一般能够不经修改或不多修改就能与UTF-8一块儿使用。
  • 使用标准的面向字节的排序例程对UTF-8排序将产生与基于Unicode代码点排序相同的结果。(尽管这只有有限的有用性,由于在任何特定语言或文化下都不太可能有仍可接受的文字排列顺序。)
  • UTF-8和UTF-16都是可扩展标记语言文档的标准编码。全部其它编码都必须经过显式或文本声明来指定。[2]
  • 任何面向字节字符串搜索算法均可以用于UTF-8的数据(只要输入仅由完整的UTF-8字符组成)。可是,对于包含字符记数的正则表达式或其它结构必须当心。
  • UTF-8字符串能够由一个简单的算法可靠地识别出来。就是,一个字符串在任何其它编码中表现为合法的UTF-8的可能性很低,并随字符串长度增加而减少。举例说,字符值C0,C1,F5至FF历来没有出现。为了更好的可靠性,可使用正则表达式来统计非法过长和替代值(能够查看W3 FAQ: Multilingual Forms上的验证UTF-8字符串的正则表达式)。
  • 与UCS-2的比较:ASCII转换成UCS-2,在编码前插入一个0x0。用这些编码,会含括一些控制符,好比"或 '/',这在UNIX和一些C函数中,将会产生严重错误。所以能够确定,UCS-2不适合做为Unicode的外部编码,也所以诞生了UTF-8。

UTF-8编码的缺点[编辑]

编写不良的解析器[编辑]

一份写得不好(而且与当前标准的版本不兼容)的UTF-8解析器可能会接受一些不一样的伪UTF-8表示并将它们转换到相同的Unicode输出上。这为设计用于处理八位表示的校验例程提供了一种遗漏信息的方式。

不利于正则表达式检索[编辑]

正则表达式能够进行不少英文高级的模糊检索。例如,[a-h]表示a到h间全部字母。

一样GBK编码的中文也能够这样利用正则表达式,好比在只知道一个字的读音而不知道怎么写的状况下,也可用正则表达式检索,由于GBK编码是按读音排序的。只是UTF-8不是按读音排序的,因此会对正则表达式检索形成不利影响。可是这种使用方式并未考虑中文中的破音字,所以影响不大。Unicode是按部首排序的,所以在只知道一个字的部首而不知道如何发音的状况下,UTF-8可用正则表达式检索而GBK不行。

其余[编辑]

与其余Unicode编码相比,特别是UTF-16,在UTF-8中ASCII字符占用的空间只有一半,但是在一些字符的UTF-8编码占用的空间就要多出1/3,特别是中文、日文和韩文(CJK)这样的方块文字。

utf8_unicode_ci和utf8_general_ci区别[编辑]

在数据库系统MySQLMariaDB中有多种字符集,其中utf8_unicode_ci和utf8_general_ci是最经常使用的,可是utf8_general_ci对某些语言的支持有一些小问题,若是能够接受,那最好使用utf8_general_ci,由于它速度快。不然,请使用较为精确的utf8_unicode_ci,不过速度会慢一些。

UTF-8的派生物[编辑]

Windows[编辑]

虽然不是标准,但许多Windows程序(包括Windows记事本)在UTF-8编码的文件的开首加入一段字节串EF BB BF。这是字节顺序记号U+FEFF的UTF-8编码结果。对于没有预期要处理UTF-8的文本编辑器和浏览器会显示成ISO-8859-1字符串

Posix系统[编辑]

Posix系统明确不建议使用字节序掩码EF BB BF[2]由于不少文本文件指望以 “#!”开头指示要运行的程序。Linux系统选择使用Unicode规范形式Normalization Form C(NFC),即优先使用预组装字符(precomposed character)而非组合字符序列(combining character sequence)。

2002年9月发布的Red Hat Linux 8.0才开始正式把大多数区域设置的默认编码设为UTF-8。此前是各类语言的但字节编码为主。2004年9月SuSE Linux 9.1开始,缺省编码迁移为UTF-8。

字符串处理时,使用UTF-8或locale依赖的多字节编码情形,比使用C语言wchar_t的宽字符固定宽度编码,要慢1至2个数量级。[2]

Java[编辑]

在一般用法下,Java程序语言在经过InputStreamReaderOutputStreamWriter读取和写入串的时候支持标准UTF-8。可是,Java也支持一种非标准的变体UTF-8,供对象的序列化Java本地界面和在class文件中的嵌入常数时使用的modified UTF-8

变种UTF-8[编辑]

标准和变种的UTF-8有两个不一样点。第一,空字符(null character,U+0000)使用双字节的0xc0 0x80,而不是单字节的0x00。这保证了在已编码字符串中没有嵌入空字节。由于C语言等语言程序中,单字节空字符是用来标志字符串结尾的。当已编码字符串放到这样的语言中处理,一个嵌入的空字符将把字符串一刀两断。

第二个不一样点是基本多文种平面以外字符的编码的方法。在标准UTF-8中,这些字符使用4字节形式编码,而在改正的UTF-8中,这些字符和UTF-16同样首先表示为代理对(surrogate pairs),而后再像CESU-8那样按照代理对分别编码。这样改正的缘由更是微妙。Java中的字符为16位长,所以一些Unicode字符须要两个Java字符来表示。语言的这个性质盖过了Unicode的增补平面的要求。尽管如此,为了要保持良好的向后兼容、要改变也不容易了。这个改正的编码系统保证了一个已编码字符串能够一次编为一个UTF-16码,而不是一次一个Unicode码点。不幸的是,这也意味着UTF-8中须要4字节的字符在变种UTF-8中变成须要6字节。

由于变种UTF-8并不是UTF-8,因此用户在交换信息和使用互联网的时候须要特别注意不要误把变种UTF-8当成UTF-8数据。

Mac OS X[编辑]

Mac OS X操做系统使用正式分解万国码(canonically decomposed Unicode),在文件系统中使用UTF-8编码进行文件命名,这作法一般被称为UTF-8-MAC。正式分解万国码中,预组合字符是被禁止使用的,必须以组合字符替换。

这种方法使分类变得很是简单,可是会搞混那些使用预组合字符为标准、组合字符用来显示特殊字符的软件。Mac系统的这种NFD数据是万国码规范化(Unicode normalization)的一种格式。而其余系统,包括WindowsLinux,使用万国码规范的NFC形式,也是W3C标准使用的形式。因此一般NFD数据必须转换成NFC才能被其余平台或者网络使用。

苹果开发者专区有关于此问题的讨论:Apple Q&A 1173

参考文献[编辑]

统一码联盟出版的书[编辑]

  • The Unicode Standard, Version 5.0, Fifth Edition, The Unicode Consortium, Addison-Wesley Professional,2006年10月27日。ISBN 0-321-48091-0
  • The Unicode Standard, Version 4.0, The Unicode Consortium, Addison-Wesley Professional,2003年8月27日。ISBN 0-321-18578-1

外部连接[编辑]

参见[编辑]

相关文章
相关标签/搜索