最近接了一个单是需要把非 UTF-8 (No BOM)编码的文件转换成 UTF-8 (No BOM),若此文件是 UTF-8 但带有 BOM ,需要转换成不带 BOM 的。于是开启了一天的阅读。首先花了一上午阅读“文件编码格式(转) - lionking - 博客园 ”这篇文章,阅读完后终于明白了“UTF-8 不是字符集”这句话。之后怕混淆再阅读了“字符集和字符编码(Charset&Encoding) - 博客 - 伯乐在线 ”确认。
这篇文章纯粹是用通俗的语言解释我以前混淆的几个概念,对于具体的规范不做解释,希望大家有像我一样疑惑的读完后不再疑惑,可以在需要时查阅正式的规范是解决需要的问题。
我们都清楚打开记事本看见一个字母 a ,它在磁盘上的二进制值是 0x61 。正是字符集让 0x61 显示成小写字母 a 。因此字符集和字符集中显示的字符是一一对应的。
我之前一直被“编码”这个词搞混了。因为在我们的日常交流或者看博客看书等都可以看见下面一些类似的话:
■“xxx 以 ASCII 编码显示为 yyy”。
■“该文件在 Windows 上以 ANSI 编码,在 Linux 下无法正常显示,需要在 Windows 上以 UTF-8 编码保存这样就可以在 Linux 下打开就不会乱码了”
■“UTF-8 不是字符集只是一种编码标准”。
这几句话给我造成了以下疑惑。
■“ASCII 和 ANSI 不是字符集么,怎么这里用来编码了”。
■“UTF-8 不是字符集,怎么可以用来编码呢?查查资料说采用 Unicode 字符集,Unicode 说是同一码,到底是个啥玩意?”。
首先,字符集(character encoding 或 character set)定义了二进制与字符的对应关系。通过字符集可以把字符转换成相应的二进制用于存储和传输,当需要阅读文字时,可以通过字符集把二进制转换成相应的字符。作为程序员我们都知道 ASCII 字符集(ASCII 码)是7位的,一共只能表示128个字符,具体内容,网上搜索都可以找到。
在继续往下写之前先在这里说明一下”什么是 ANSI 字符集,它和 ASCII 字符集有什么关联?“。
我最初见到提及”ANSI 字符集“的地方是在《Windows 程序设计》这本书中。书中前面就介绍 ANSI 字符集和 Unicode 字符集,可惜我当初看了几遍也没有看出个所以然,还有就是用 VC 写程序时,在”项目设置“有一项设置”字符集“,是个下拉框可以选择”ANSI 字符集“和”Unicode 字符集“。当时我也不知道到底是什么意思,反正只知道书中建议程序员多用 Unicode 程序集。所以我对于 ANSI 字符集和 Unicode 字符集的印象一直都是多用 Unicode 字符集。为什么说印象呢?因为我不知道具体是啥。
其实没有具体的某个字符集叫 ANSI 字符集,它只是在 Windows 上面的一个统称。ANSI 字符集应该来源于 ANSI 代码页(代码页其实就是字符集,只是早期的叫法而已)。因为 Windows 代码页最初是根据 ANSI 草案实现的,所以 Windows 代码页又叫做 ANSI 代码页。之后字符集这个词出现了,应该就把这些代码页叫做 ANSI 字符集了吧。在维基百科上面有关于代码页的介绍。ASCII 字符集是7位的,一共只能表示128个字符,表示其他语言就成了问题,后来对 ASCII 字符集进行了扩展,用两个字节表示其他语言,这样就绰绰有余了。比如简体中文 GB2312,繁体中文 big5,日语 SHIFT_JIS 。举个比较通俗(没有那么准确)的例子,在中国大陆的 Windows 电脑中 ANSI 字符集是指 GB2312 ,在中国台湾 Windows 电脑中 ANSI 字符集是指 big5,在日本 Windows 电脑中 ANSI 字符集是指 SHIFT_JIS 。就是这么个概念。这些字符集中每个字符都是以2字节编码,因此这种字符集又叫 DBCS 。
接着。以上我产生了两个疑惑。其实第一个疑惑完全是被表述误解。比如根据 ASCII 字符集把 0x61 显示为 a ,或者把 a 存储为 0x61 ,这句话也有人习惯表述为把 0x61 用 ASCII 编码为 a ,用 ASCII 把 a 解码为 0x61 。其实都是一个意思。因此关于编码这个词应该根据上下文语意来进行理解。
第二个疑惑是把 Unicode 字符集与 ASCII 字符集、ANSI 字符集和 GB2312 字符集搞混淆了。前面我们提到的 ANSI 字符集以2字节编码,比如在简体中文中 0x1234 是指某个字,到了繁体中文就成了另外一个字,到了日语又是一个日语字符。Unicode 是一个统称,当提到 Unicode 字符集其实是指 Universal Character Set, UCS 。我们可以说 Unicode 字符集,当然也可以说 UCS 字符集。UCS 字符集包括了其他所有语言的文字,比如拉丁语,简体中文,繁体中文,日语,韩语等。也就是说不同的语言中的不同字符在 UCS 中都一个唯一的位置,而不是像 ANSI 字符集那样,2字节在不同的地方表示不同的语言文字。这样 UCS 就实现了所有语言的大一统,当然 UCS 的字节数不止2字节,UCS 分为 UCS-2 和 UCS-4 ,分别表示占用2字节时表示的字符和占用4字节时占用的字符,在 UCS-2 前面加2个值为0的字节就变成了 UCS-4 。
我们明白了 Unicode 字符集,但是 Unicode 字符集在日常使用时并非直接使用字符对应的二进制,而是会对这个二进制再次编码后使用,或用于网络传输或用于磁盘保存。这种编码方法就叫做 UTF , Unicode Transformation Format 。
常见的 UTF 编码有 UTF-32,UTF-16,UTF-8 。UTF-8 由于特殊的算法是不需要额外声明字节序,但 UTF-32 或者 UTF-16 则不行,如果我们收到 UTF-16 字节流“594E”,不知道它的值是 0x594E 还是 0x4E59,这是”奎“还是 ”乙“?Unicode 规范中推荐的标记字节顺序的方法是 BOM ,Byte Order Mark。在UCS 编码中有一个叫做”ZERO WIDTH NO-BREAK SPACE“的字符,它的值是 0xFEFF,而 0xFEFF 在 UCS 中是不存在的字符。这样如果接收者收到 FEFF ,就表明这个字节流是 Big-Endian 的;如果收到 FFFE ,就表明这个字节流是 Little-Endian 的。UTF-8 不需要 BOM来表明字节顺序,但可以用 BOM 来表明编码方式。字符”ZERO WIDTH NO-BREAK SPACE“的UTF-8编码是 EF BB BF 。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。
总结:
1)
ASCII 字符集是7位标识拉丁语的字符集。
Unicode 字符集就是指 UCS 字符集,它是统一定义了所有语言中的字符(目前绝大部分语言都有包括进去)。
ANSI 字符集是在 Windows 上面的一个统称,这些字符集是2字节编码,由 ASCII 字符集拓展而来。简体中文 GB2312 ,繁体中文 big5 都可以划分到这个范畴。
2)
代码页就是字符集。只是早期的叫法而已。
3)
UTF 是 Unicode 字符集的传输格式,这里传输是翻译成中文的叫法,可以在网络上传输也可以在磁盘上保存文件。UTF 本身不是字符集,是基于 Unicode 字符集。
引用:
1) 百度百科。ANSI 编码。
http://baike.baidu.com/view/1273097.htm。
2) 维基百科。Unicode。
https://zh.wikipedia.org/wiki/Unicode。
4) 维基百科。代码页。
https://zh.wikipedia.org/wiki/%E4%BB%A3%E7%A0%81%E9%A1%B5#Windows.EF.BC.88ANSI.EF.BC.89.E4.BB.A3.E7.A2.BC.E9.A0.81。
(注:维基百科中文已被墙!)