Text Representation (文本表示方法）

（以下内容，译自Category:Tutorials中的Text Representation. 更多内容可以参看Category:Chinese. ）

一般情况下文本处理是相当简单的,至少似乎是相当简单的. 不过,我们也常常会碰到一些情况,还真不是一般的复杂.

这个专题中就来说说这个“不一般”.

不过,我们还是从最简单的开始:


 * 文本是字符序列.

正是这样简单的看法,使得大多数文本处理相当简单. 如果还是单一的编程语言,单一的机器,单一的用户,那么这一切都还能对付.

不过,传输文本的东西常常会中断. 因为有问题:
 * 序列究竟是什么?
 * 字符说到底又是什么?

序列
序列就是一个字符跟着一个字符. 麻烦的是确定序列何时结束. 在一个文件中,这就是文件的结尾. 其它情况下可能是固定数量的字符. 在Visual Prolog中(与在C/C++中一样)串通常是由零结尾的,这意味着零是保留字符,它不会在正常文本中出现. 所以,零可以用来表示一个串的结尾,也就是在串的最后一个实际字符后放一个零,表示这个串到此为止. 这个表示法在8比特串和16比特串中都适用.

COM(以及Visual Basic)使用的串表示法,是在串的起始放置一个32比特的数,这个数就是后跟的串字符个数. 这种方法只用于16比特串. 由于串的长度是已知的,这样的串可以包含“零”这个字符,甚至可以包含无意义的字符. 这有时还是有用的(只要可能,总会有人加以利用).

字符集
在以往,内存和外存都是很昂贵的,而不同文化间的数据交流并不多. 今天,内存外存都很便宜了(按单位存储量计算), 而不同文化间的数据交换也是很普遍的事了.

在这样的变化过程中,有过很多种文本表示方法,有的还在用,有的就被遗忘了.

表示字符的方法可以分为如下三类:


 * 单字节字符集 : 每个字符用一个字节(8比特的数)表示
 * 多字节字符集: 每个字符用一个或多个字节表示
 * 宽字符集: 每个字符用一个或多个字(16比特的数)表示

单字节字符集是多字节字符集的一种特殊情况,就是不用“或多字节”的情况. 之所以还要说一下单字节字符集,是要用它来介绍一下“代码页”的概念. 代码页是微软在处理字符集时用的一个概念. 代码页是数字与其所表示的字符间的映射,比如说,数字87表示字符'W'. 代码页有很多种,主要用来处理不同的语言文字,但也还有其它一些原因(不同的群体使用各自的表示法).

因此,代码页描述的是各字符编码的字节. 各个代码页都有一个名字和一个标识(一个数字)以便使用时指明用的是哪个字符集.

微软还把代码页名称与代码页标识通用化地用于并非基于代码页的字符集. 比如,多字节字符集也有相应的代码页名和代码页标识,尽管其编码不是用一个代码页描述的.

而 宽字符集 的概念只用于Unicode,我们在下一节中讨论.

Unicode
Unicode定义了一种字符集,不过这个字符集有好几种表示方法. 人们常会被 Unicode的这些表示方法搞糊涂,不过只要搞清楚它们的意思就能很好地理解它们了. 我要为这种理解做点儿贡献,仔细听我说:


 * Unicode是一个“抽象”的字符集,它有若干种可选的表示方法.

Unicode字符集分成17个版面，每个版面包含有65536个 代码点. 这样，一共就有17*2^16 = 1114112个代码点. 每个代码点可以表示一个字符，不过有些代码点被保留用作特殊用途. 到目前为止很多这样的代码点并没有被规定（也就是可以留作以后使用）.

如果我们把代码点写成十六进制数的话，它的分布是0x000000到0x10FFFF. 也就是说，头两位十六进制数表示了版面而剩下的四位表示该版面中的代码点.

utf32 表示用32比特的数来代表那个范围(0-0x10FFFF)的各个代码点. 这很少用于多个字符，因为每个字符要占32比特而其中有11比特总是0. 但它用于处理单个字符时还是比较方便的,一个字符可以当作单独一个数.

可能有人会奇怪(至少我是有过疑问):为什么是17个版面?13个或16个就不行了吗?现在还有10个版面没用. 其原因(同时也可以解释其它许多情况)是:尽管“ Unicode是一种具有多个表示方法的抽象字符集”,但它实际上是设计用于一种特定的宽字符表示方法. 其实,它是设计用于一种特定的16比特字符表示法的. 在这个设计中,最常用的字符占用16比特数而所有其它的则使用两个16比特数,叫替身对(或就叫替身).

对于这种表示法,会是这样:当在一个串中看一个单个16比特数时,你可以确定:
 * 它是否是常用的单字(one-wor)字符
 * 它是否是替身对的第一个数
 * 它是否是替身对的第二个数

它还可以是字节顺序标记,这也可以区分不同的东西(或是把其它的归入特定的类).

所以Unicode的设计是用16比特做大多数事而其余的则用32比特办,同时它又可以立即区分出这些不同的情况.

这种特殊的宽字符表示法就是Windows使用的方法,也是Visual Prolog使用的.

utf16和utf16BE
计算机之间的通信总是基于字节序列的. 因此,如果一台计算机写出字节序列 0x01 0x02 0x03 0x04 那任一台别的计算机也应该读如 0x01 0x02 0x03 0x04.

多字节字符集用一个或多个字节来表示字符. 由于它定义了计算机传输时字节的顺序,所以可以得到期望的结果.

但当传输宽字符集时,每个宽字符需要传两个字节,此时就需要确定先传哪个. 如果有一个Unicode串,它是用宽字符格式编码的,还带有前面说的替身对,对每一个16比特数,先传最低位字节再传最高位字节,这就是utf16格式(它在HTML应用时就被称为Unicode). 反过来,要是先传最高位字节再传最低位字节, 就是 utf16BE 格式(utf16 big-endian的缩写).

所以,在Windows计算机中宽字符串的utf16格式与多字节串是一样的. 不过要注意,8比特串程序对这样的串是不能正常处理的,因为utf16串中可以包含“零”这个字节, 而这在8比特串中表示串的结尾.

用utf16或utf16BE格式写文件时,可以在文件起始处写一个字节顺序标识,这个标识是数0xFEFF. 如果是小结尾模式,第一个字节应该是0xFF而第二个字节应该是 0xFE;如果是大结尾模式则顺序与小结尾模式刚好相反. 如此一来,这两个字节就可以反映出文件是小结尾还是大结尾格式. 不过,需要注意无论是0xFEFF还是 0xFFFE都不能在utf16和utf16BE中有别的用途. 它只能是用户定义的文件中的字节顺序标识,而不能是其它的,比如说不能用于WEB服务器传送HTML页时的字节顺序. 大多数浏览器使用的是两个奇怪的字符来表示字节顺序标识.

utf8
utf8 是另一种有意思的Unicode多字节编码. utf8每个代码点使用1-6个字节,因此宽字符(utf16)中的替身对先转换成(utf32)代码点然后再转换成utf8. 当从任一字节开始处理一个utf8串时,在下一个代码点一定可以得到解释,也就是说最多只需要略过当前多字节字符的其余部分就可以得到同步.

utf8编码不包含“零”字节,所以可以用零做结尾.

在7比特ASCII码中的那些字符可以用单字节utf8表示,而其它字符就需要使用较多字节. 因此,西方语言的文本表示起来就相当紧凑,而其它语言用utf16表示时比较紧凑.

utf8文件也可以包含一个字节顺序标识 0xEF 0xBB 0xBF (它不是用来表示字节顺序的,而是用来表 utf8 格式).

Category:Basic Information