首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

Unicode编码

Unicode Encodings

有许多编码定义了Unicode数据的存储方式,包括定长和变长两种。一个定长编码,是指每个码点包含固定的字节数;而一个可变长度编码,是指不同的字符可以用不同数量的字节来表示。UTF-32和UCS2是定长的,UTF-7和UTF-8是可变长度的, 而UTF-16是一种可变长度编码,但通常看起来像一个定长编码。

UTF-32(以及UCS4,两者几乎相同)编码每个码点用4个字节,因此可以在U+0000和U+FFFFFFFF之间对任何码点编码。这通常没有必要,因为根本没有定义那么多的码点。UCS2用2个字节编码每个码点,因此可以使用介于U+0000和U+FFFF之间的内容编码任何码点。UTF-16对于大多数字符也使用2个字节,但在U+D800和U+DFFF之间

被当作所谓的代理对来使用,它使得UTF-16能使用U+0000和U+10FFFF之间的值来对码点进行编码。

UTF-8使用1到4个(ISO 10646版本是1到7,接下来会讨论这个版本)字节来为码点编码,而且能够编码介于U+0000和U+0FFFF之间的码点(ISO 10646版本是U+0000到U+3FFFFFFFFFF)。待会儿我们将会更详尽地讨论UTF-8。UTF-7是7位安全编码,它能够直接用于电子邮件编码,而不需要通过base64或quoted-printable编码进行转换。UTF-7从未真正流行和被广泛使用,因为它缺乏UTF-8的ASCII码透明性,而且quoted-printable已经足以应付电子邮件传输UTF-8编码的需要了。

那么,我们一直提及的ISO 10646是什么? Unicode显然是一个好主意,所以有两个组织同时开展这方面的工作,分别是Unicode联盟和国际标准化组织(ISO)。在发布前,标准进行了合并,但仍然保留了单独的名字。随着时间的推移,它们之间大多保持同步,但在编码问题里有不同的文档和一些分歧。为保持清晰,我们将它们视为同一个标准。

最重要的一点是,我们虽然有多种编码方式(将码点映射到字节),但我们只有一个单一的字符集(将字符映射到码点)。这是Unicode核心的概念。有一组单一的码点可以供所有应用程序使用,提供多重编码方式,使应用程序按照合适的方式存储数据。所有的Unicode编码都是完整的,我们可以随时从一个转换到另一个,而不丢失任何信息(这里不考虑这个事实:许多私用的码点不能用UTF-16表示,而UTF-32可以)。在Unicode中,无论具体采用什么编码来存放它,码点U+09E0总是代表孟加拉语的元音字符RR。

码点和字符, 字形和字素

Code Points and Characters, Glyphs and Graphemes

到目前为止,我们已经描绘出一幅相当复杂的画面——字母是有公认含义的标识符,并且通过码点来表示。一个码点可以用一个或者多个字节通过某种编码来表示。但实际情况要更为复杂。一个字符并不一定反映人们日常所说的字符。例如,带有发音符号的拉丁字母“a”可以用码点U+00E3(带有发音符号的拉丁小写字母“a”)来表示,也可以用两个码点来组合表示,U+0061(拉丁小写字母“a”)和U+0303(加上发音符号)。这种组合形式被称为字素。一个字素由一个或者多个字符组合而成——基本字符加零个或多个附加字符。再加上连字,情况就更复杂了,这时一个单独的字形可能由两个或者三个字符形成。这些字符接着由一个单独的码点,或者两个正常的码点来表示。比如,连字fi(“f”后面跟“i”)可以用U+0066(小写拉丁字母“f”)和U+0131(不带点的小写拉丁字母“i”)

来表示,或者由U+FB01(小写的拉丁连字“fi”)来表示。

这些在实际应用上意味着什么? 这意味着给定一串码点流,不能随意对它们分段(就像substring函数那样)以得到预期的音素序列。这也意味着有超过一种的方法来表示单个字母,可用不同序列的连字符和字符相结合,来形成相同的音素(虽然Unicode标准化规则允许性能良好的分解状态的音素比较)。想要知道使用UTF-8编码的字符串中的字符数(长度)时,我们不能依赖于字节数。我们甚至不能依赖于码点,因为有一些码点可组合成不增加额外字形的字符。你需要了解这两方面的内容,一是码点位于字节流何处,二是码点属于什么字符类别。将Unicode中定义的字符类别列于表4-1

表4-1:Unicode基本分类

Codes

Descriptions

Lu

Letter, uppercase

Ll

Letter, lowercase

Lt

Letter, titlecase

Lm

Letter, modifier

Lo

Letter, other

Mn

Mark, nonspacing

Mc

Mark, spacing combining

Me

Mark, enclosing

Nd

Number, decimal digit

Nl

Number, letter

No

Number, other

Zs

Separator, space

Zl

Separator, line

Zp

Separator, paragraph

Cc

Other, control

Cf

Other, format

Cs

Other, surrogate

Co

Other, private use

Cn

Other, not assigned(including noncharacters)

Pc

Punctuation, connector

Pd

Punctuation, dash

Ps

Punctuation, open

Pe

Punctuation, close

续表

Codes

Descriptions

Pi

Punctuation, initial quote(may behave like Ps or Pe depending on usage)

Pf

Punctuation, final quote(may behave like Ps or Pe depending on usage)

Po

Punctuation, other

Sm

Symbol, math

Sc

Symbol, currency

Sk

Symbol, modifier

So

Symbol, other

事实上,Unicode不仅仅确定了每个字符的大类。它还规定了姓名、一般特性(英文字母和方块等)、成形信息(双向、镜似等)、字体(大写、小写等)、数值、标准化属性、边界和其他全部有用的资料。这些多半不是我们关注的内容,而使用这些信息时,我们也意识不到正在用它们。因为这一切都在幕后神奇地进行,但值得注意的是,除了码点本身之外,Unicode标准的核心部分就是这些属性。

这些属性和特点,连同标准化规则,都可以从Unicode协会网站(http://www.unicode.org/)获得,它同时提供了人和计算机可读的格式。

字节顺序标记

Byte Order Mark

一个字节顺序标记(BOM)是一组字节序列,它们出现在Unicode流开端,说明编码类型。因为系统可能是大尾字节序(big endian),也可能是小尾字节序(little endian),或者是多字节的Unicode编码,例如UTF-16可采用任何一种方式来储存组成码点的多个字节(最高或最低字节优先)。BOM把码点U+FEFF(为此目的预留)置于文件开端,利用它来判断字节序。字节的实际输出取决于使用的编码,在读取Unicdoe流的头4个字节后,就可以确认所用的编码(见表4-2)。

表4-2:常用Unicode编码的BOM

Encoding

Byte order mark

UTF-16 big endian

FE FF

UTF-16 little endian

FF FE

UTF-32 big endian

00 00 FE FF

UTF-32 little endian

FF FE 00 00

UTF-8 little endian

EF BB BF

大多数其他的Unicode编码有自己的BOM(包括SCSU、UTF-7和UTF-EBCDIC),也都表示码点U+FEFF。BOM应避免用在HTML和XMLdocuments的开端,这会让一些浏览器无法正常解析。因为PHP不会接受,所以您也应该避免在PHP模板或源代码文件的开端放置BOM,即使它们可能采用了UTF-8编码。

想要更多关于Unicode标准的资料,您可以访问Unicode协会的Unicode网站,网址是http://www.unicode.org/,或者再买本书——《Unicode标准4.0》(Addison-Wesley)(这本书很有趣,它包含了当前所有的98 000个Unicode码点),你可以从http://www.unicode. org/book/bookform.html订购这本书。

UTF-8编码

The UTF-8 Encoding

UTF-8是大多数Web应用开发人员喜爱的编码方式,其全名是Unicode 8-bit转换格式. UTF-8是一个可变长度的编码,适合用于紧凑地存储拉丁字母。对那些字母,它比大固定宽度编码(如UTF-16码)节省空间,而且也能支持大范围的码点。UTF-8和ASCII (又称ISO 646标准)完全兼容。因为ASCII编码定义的代码只有0到127 (使用字节的头7位),UTF-8原样保留所有这些编码,而将高位比特则用于更高的码点。

UTF-8将码点的相应表示的长度(按字节计)编码到首字节中,然后使用后续的字节添加相应长度的表示码点的比特。UTF-8字节编码序列中,每个字节都贡献0至7比特给最终的码点,总体上形成从左到右的一个长二进制数字。这些字节基于表 4-3组合成每个码点的二进制表示。

图4-3:UTF-8 字节布局

字节数

位数

表示

1

7

0bbbbbbb

2

11

110bbbbb 10bbbbbb

3

16

1110bbbb 10bbbbbb 10bbbbbb

4

21

11110bbb 10bbbbbb 10bbbbbb 10bbbbbb

5

26

111110bb 10bbbbbb 10bbbbbb 10bbbbbb 10bbbbbb

6

31

1111110b 10bbbbbb 10bbbbbb 10bbbbbb 10bbbbbb 10bbbbbb

7

36

11111110 10bbbbbb 10bbbbbb 10bbbbbb 10bbbbbb 10bbbbbb 10bbbbbb

8

42

11111111 10bbbbbb 10bbbbbb 10bbbbbb 10bbbbbb 10bbbbbb 10bbbbbb 10bbbbbb

这意味着对于码点U+09E0(我最喜欢的孟加拉元音字幕RR),我们要用三个字节,因为它需要表示12个比特的数据(十六进制的09E0在二进制中是100111100000)。我们将这些比特的数据和比特掩码组合起来就得到11100000 10100111 10100000 或者说0xE0 0xA7 0xA0 (也许你已经可以从前例中认识到这点)。

UTF-8设计之中的一个优点是它将一组码点编码成字节流,而不是WORDS或者DWORDS,这样可以忽略底层机器的字节序(endian)问题。这意味着你可以在两台小尾字节序和大尾字节序的机器之间交换UTF-8流而不需要任何字节重组或添加BOM。也就是说,你可以完全无视底层的体系架构。

UTF-8编码的另一个优点源于它从左到右存储实际码点的比特,做一个二进制形式的原始字节排序,就可以将字符串按码点排序。这虽然不如按locale排序规则进行排序那么好,但对于无需理解UFF-8的底层系统来说,它终究提供了很简易的一种排序方式,底层系统只需要知道如何排序原始字节就可以了。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论