刨根究底字符编码之十一——UTF-8编码方式与字节序标记

UTF-8码方式与字节序标记

 

 

1、UTF-8码方式正则表达式

1.算法

接下来将分别介绍Unicode字符集的三种码方式:UTF-八、UTF-1六、UTF-32。这里先介绍应用最为普遍的UTF-8。浏览器

为知足基于ASCII、面向字节的字符处理的须要,Unicode标准中定义了UTF-8码方式。UTF-8应该是目前应用最普遍的一种Unicode码方式(但不是最先面世的,UTF-16要早于UTF-8面世)。它是一种使用8位码元(即单字节码元)的变宽(即变长或不定长)码元序列的码方式。函数

因为UTF-16对于ASCII字符也必须使用两个字节(由于是16位码元)进行码,存储和处理效率相对低下,而且因为ASCII字符通过UTF-16码后获得的两个字节,高字节始终是0x00,不少C语言的函数都将此字节视为字符串末尾从而致使没法正确解析文本。性能

所以,UTF-16一开始推出的时候就遭到不少西方国家的抵制,大大影响了Unicode的推行。因而后来又设计了UTF-8码方式,才解决了这些问题。编码

2.url

UTF-8的码元由8位单字节组成;在UTF-8中,由于码元较小的缘故,Unicode码点值被映射到一个、两个、三个或四个码元;换言之,UTF-8使用一个至四个8位单字节码元的序列来表示Unicode字符。spa

UTF-8码方式对全部ASCII码点值(0x00~0x7F)具备透明性。所谓透明性,具体指的是在U+0000到U+007F范围内(十进制为0~127)的Unicode码点值,被直接转换为UTF-8单一字节码元0x00~0x7F,与ASCII码没有区别。操作系统

而且,0x00~0x7F不会出如今UTF-8码的非ASCII字符的首字节与非首字节的任意一个字节中(非ASCII字符的UTF-8码为由多个单字节码元所组成的码元序列),这样就保证了与早已应用普遍且已成为工业标准的ASCII码的彻底兼容,避免了歧义,同时纠错能力也强。设计

 

笨笨阿林原创文章,转载请注明出处)

3.

UTF-8同其余的多字节码元码方式相比具备如下优势:

a)  UTF-8的码空间足够大,将来Unicode新标准收录更多字符,UTF-8也能适应,所以不会再出现UTF-16那样的尴尬。

(注:这里所指的码空间并非前文所提到的号空间Code Space,号空间属于号字符集CCS里的概念,而码空间属于字符码方式CEF里的概念,二者不能等同;这里的码空间可理解为码方式的将来可扩展性、高适应性,详见后文《UTF-8到底是怎么码的——UTF-8的编码算法介绍》以及《UTF-16到底是怎么码的——UTF-16的编码算法介绍》)

b)  UTF-8是变长码(准确地说是变长码元序列,而码元自己是固定长度为8位单字节的,也就是说,UTF-8采用的单字节码元),好比一个字节足以容纳全部的ASCII字符,就用一个字节来存储,没必要在高位补0以浪费更多的字节来存储,所以在英语做为国际语言的现实状况下,UTF-8因其ASCII字符的单字节码这一特性可节省空间。

c)  UTF-8彻底直接兼容ASCII码,而非不彻底间接兼容。

d)  UTF-8的码元序列的第一个字节指明了后面所跟的字节的数目(即带有前缀码),这对字节流的前向解析很是有效(详见后文《UTF-8到底是怎么码的——UTF-8的编码算法介绍》)。

e)  也由于UTF-8码带有前缀码,因此容错性好,即便在传输过程当中发生局部的字节错误,好比即使丢失、增长、改变了某些字节,也不会致使全部后续字符所有错乱这样传递性、连锁性的错误问题(不然,若存在错误传递性、连锁性的话,一旦中间某些字节出错,则必须丢弃从出错点开始到结尾的全部码字节,好比GB码、UTF-32码就是如此),所以很容易从新同步,具备很强的鲁棒性(即健壮性)。

f)  因为UTF-8码没有状态,从UTF-8字节流的任意位置开始能够有效地找到一个字符的起始位置,字符边界很容易界定、检测出来,因此具备很好的“自同步性”。

g)  UTF-8已经成为互联网所采用的字符码方式的事实标准。

h)  UTF-8是字节顺序无关的(由于是单字节码元,而非像UTF-1六、UTF-32这样的多字节码元),它的字节顺序在全部系统中都是同样的,其码元序列与字节序列相同,所以它实际上并不须要字节顺序标记BOM(Byte-Orde Mark),虽然Windows系统常常“画蛇添足”地加上BOM。(有关字节序标记BOM的介绍见下文)

字节序问题在进行信息交换时会带来不小的麻烦。若是字节序未协商好,将致使乱码;若协商结果为双方一个采用大端一个采用小端,则必然有一方要进行大小端转换,性能损失不可避免(字节序的大小端问题其实不像看起来那么简单,有时会涉及硬件、操做系统、上层应用软件多个层次,可能会致使屡次转换,详见前文中有关字节序Byte-Orde的介绍)。

i)  字节FE(二进制为1111 1110)和FF(二进制为1111 1111)在UTF-8码中永远不会出现(由于UTF-8码方式中,每一个字节只能以0、1十、11十、11110或10开头,详见后文介绍)。所以能够用称之为零宽度不中断空格(ZERO WIDTH NO-BREAK SPACE)的字符(Unicode字符名称为U+FEFF)做为字节顺序标记BOM来标明UTF-16或UTF-32文本的字节序。

(Windows系统中BOM有时也用在UTF-8码的文本文件的开头,虽然UTF-8码不存在字节序问题,但Windows却用BOM来代表该文本文件的码格式为UTF-8,看起来这有点“画蛇添足”,其具体缘由详见后文)

j)  UTF-8码能够经过屏蔽位和移位操做快速读写。

k)  字符串比较时strcmp()和wcscmp()的返回结果相同,所以使排序变得更加容易。

4.

UTF-8码方式也并不是天衣无缝,大体上有以下缺点:

a)  没法根据字符数直接判断出UTF-8文本的字节数,由于UTF-8是一种变长码方式(码元虽然固定为8位单字节,但码元序列是变长的,多是单个码元共8位,好比ASCII字符;也多是两个码元共16位、三个码元共24位、四个码元共32位等)。所以,不管是计算字符数,仍是执行索引操做,效率都不高。

b)  须要用2个字节码那些在扩展ASCII(即EASCII)字符集中只需1个字节码的扩展字符。

c)  以8位单字节码元码的UTF-8字符会被Email网关过滤,由于Internet上的信息传输最初设计为7位ASCII码字符(ASCII仅用到了1个字节的低7位)的传输。所以产生了UTF-7码(相似于一样为Email传输而设计的Base64码或quoted-printable码,因为Base64码或quoted-printable码各有其不足,所以又设计了UTF-7码)。

d)  UTF-8在它的表示中使用值100xxxxx的概率超过50%,而现存的实现如ISO 202二、487三、6429和8859系统,会把它错认为是C1控制码。所以产生了UTF-7.5码。

笨笨阿林原创文章,转载请注明出处)

 

2、字节序标记BOM

 

1.

在将逻辑形式的码元序列(或可称之为逻辑码)映射为物理形式的字节序列(或可称之为物理码)时,因系统平台的差别,存在一个字节序(Byte-Order字节顺序)的问题。Unicode/UCS规范中推荐的标记字节顺序的方法是BOM字节序标记(Byte-Order Mark字节顺序标记)。

字节序标记BOM是Unicode码点值为FEFF(十进制为65279,二进制为1111 1110 1111 1111)的字符的别名。

最初,字符U+FEFF若是出如今字节流的开头,则用来标识该字节流的字节序——是高位在前仍是低位在前;若是它出如今字节流的中间,则表达为该字符的原义——零宽度不中断空格(ZERO WIDTH NO-BREAK SPACE零宽度无断空白)。该字符名义上是个空格,其实是零宽度的,即至关因而不可见也不可打印字符(日常使用较多的是ASCII空格字符,是非零宽度的,须要占用一个字符的宽度,为可见不可打印字符)。

从Unicode 3.2开始,U+FEFF只能出如今字节流的开头,且只能用于标识字节序,就如它的别名——字节序标记——所表示的意思同样;除此之外的用法已被舍弃。取而代之的是,使用U+2060来表示零宽度不中断空格。

2.

若是UTF-16码的字节序列为大端序,则该字节序标记在字节流的开头呈现为0xFE 0xFF;若字节序列为小端序,则该字节序标记在字节流的开头呈现为0xFF 0xFE。若是UTF-32码的字节序列为大端序,则该字节序标记在字节流的开头呈现为0x00 0x00 0xFE 0xFF;若字节序列为小端序,则该字节序标记在字节流的开头呈现为0xFF 0xFE 0x00 0x00。

UTF-8码自己没有字节序的问题,但仍然有可能会用到BOM——有时被用来标示某文本是UTF-8码格式的文本;再强调一遍:在UFT-8码格式的文本中,若是添加了BOM,则只用它来标示该文本是由UTF-8码方式码的,而不用来讲明字节序,由于UTF-8码不存在字节序问题。

3.

许多Windows程序(包含记事本)会添加BOM到UTF-8码格式的文件中(至于为何要添加BOM,可参看后续《微软跟联通有仇?》一文)。然而,在类Unix系统中,这种做法则不被建议采用。

由于它会影响到没法识别它的程语言,如gcc会报告源码文件开头有没法识别的字符;而在PHP中,若是没有激活输出缓冲(output buffering),它会使得页面内容开始被送往浏览器(即header头被提交),这使PHP脚本没法指定header头(HTTP Header)。

对于已在IANA注册的字符编码(这里的字符编码实际为字符编码模式CES)UTF-16BE、UTF-16LE、UTF-32BE和UTF-32LE等来讲,不可以使用BOM。由于其名称自己已决定了其字节顺序。对于已注册的字符编码(这里的字符编码实际为字符编码方式CEF)UTF-16和UTF-32来讲,则必须在文本开头使用BOM。

4.

不一样码的字节序列中所使用的字节序标记BOM自己的字节序列呈现:

 

笨笨阿林原创文章,转载请注明出处)

 

3、小结

1.

因为UTF-8码方式以一个字节(8位)做为码元,属于单字节码元,在计算机处理、存储和传输时不存在字节序问题(字节序问题只跟多字节码元有关),所以避免了平台依赖性,跨平台兼容性好。

它相对于其余码方式对英语更为友好,一样也对计算机语言(如C++、Java、C#、JavaScript、PHP、HTML等)更为友好。它在处理ASCII等经常使用字符集时不多会比UTF-16低效。

2.

因此,UTF-8是较为平衡、较为理想的Unicode码方式。虽然Windows平台因为历史的缘由API缺少对UTF-8的原生支持(Windows原生支持的是UTF-16,由于UTF-16早于UTF-8面世),致使UTF-8推出后的早期使用不广,但目前是应用最为普遍的三大UTF码方式之一。

所以,应该尽可能使用UTF-8(准确地说,应该尽可能使用UTF-8 without BOM,即不带字节顺序标记BOM的UTF-8)。

笨笨阿林原创文章,转载请注明出处)

(未完待续)

 

预告:《刨根究底字符码》系列的下一篇将重点剖析UTF-8到底是怎么码的(即UTF-8的编码算法介绍);而《刨根究底正则表达式》系列的下一篇为正则表达式简介(上),包括正则表达式的概念、功能、简史、流派介绍,敬请关注!】

相关文章
相关标签/搜索