字符编码详解
随着计算机普及,各国为适应本国语言各自设计编码,同一个二进制数字可能被解释成不同符号。为解决这种不兼容,Unicode 应时而生。
一、Unicode
Unicode 又称统一码、万国码、单一码,为每种语言中的每个字符设定统一且唯一的二进制编码,以满足跨语言、跨平台的文本转换与处理。可以把它想象成“字符大容器”:世界上所有符号都在里面,每个符号有独一无二的编码,从根上解决乱码。所以Unicode 是一种所有符号的编码[2]。
它伴随通用字符集标准发展,也以书本形式发表,对世界上大部分文字系统做了整理和编码。Unicode 仍在增修,迄今已收入超过十万个字符,广泛用于软件的国际化与本地化。
传统编码的共同问题是无法很好支持多语言环境,这在互联网上不可接受。几乎所有电脑都支持基本拉丁字母,并各自支持其他编码。Unicode 为兼容它们,首 256 个字符保留给 ISO 8859-1,西欧语系转换不必特别考量;大量相同字符也重复编到不同码位,使旧编码能与 Unicode 互转而不丢信息[1]。
二、实现方式:UTF
一个字符的 Unicode 码是确定的,但传输时因平台设计和节省空间,实现方式不同。实现方式称为Unicode 转换格式(Unicode Transformation Format,UTF)[1]。Unicode 是字符集,主要有 UTF-8、UTF-16、UTF-32。目前主流是 UTF-8,下面着重介绍它。
UCS
UCS(Universal Character Set,通用字符集)由 ISO 制定的 ISO 10646(或 ISO/IEC 10646)定义,包含其他所有字符集,保证双向兼容:把任意字符串译到 UCS 再译回原编码,不会丢信息。
UCS 不仅给每个字符一个代码,还赋予正式名字。表示 UCS 或 Unicode 值的十六进制数前面通常加 “U+”,例如 “U+0041” 代表 “A”。
Little endian 与 Big endian
平台对字节顺序理解不同,同一字节流可能被解释成不同内容。例如某字符十六进制 4E59,拆成 4E、59。若从低位读会当成 594E,找到“奎”;Windows 从高字节读是 4E59,找到“乙”。Windows 里存的“乙”到另一端序平台就变成“奎”。Unicode 用大头(Big endian)、小头(Little endian)区分:第一个字节在前是大头,第二个字节在前是小头。
计算机怎么知道文件用哪种端序?规范规定文件最前面加入表示编码顺序的字符,名叫“零宽度非换行空格”(ZERO WIDTH NO-BREAK SPACE),用 FEFF 表示。正好两个字节,且 FF 比 FE 大 1。头两个字节是 FE FF 表示大头;是 FF FE 表示小头。
三、UTF-8
UTF-8 是针对 Unicode 的可变长度编码,可用 1~4 个字节表示一个符号(标准还可到 6 字节的历史形式)。第一个字节仍与 ASCII 兼容,原先处理 ASCII 的系统几乎不用改。因此它逐渐成为邮件、网页及其他存文字场合优先采用的编码。
编码规则:
-
单字节符号:第一位为 0,后 7 位是 unicode 码。英文字母的 UTF-8 与 ASCII 相同。 -
n 字节符号(n>1):第一个字节前 n 位为 1,第 n+1 位为 0;后面字节前两位一律 10。其余位填这个符号的 unicode 码。
| Unicode | UTF-8 |
|---|---|
| 0000 ~ 007F | 0XXX XXXX |
| 0080 ~ 07FF | 110X XXXX 10XX XXXX |
| 0800 ~ FFFF | 1110XXXX 10XX XXXX 10XX XXXX |
| 1 0000 ~ 1F FFFF | 1111 0XXX 10XX XXXX 10XX XXXX 10XX XXXX |
| 20 0000 ~ 3FF FFFF | 1111 10XX 后接四个 10xxxxxx |
| 400 0000 ~ 7FFF FFFF | 1111 110X 后接五个 10xxxxxx |
读表很简单:第一个字节第一位若为 0,这一字节单独就是一个字符;若为 1,连续多少个 1 就表示占多少个字节。
以汉字“严”为例[3]。已知 unicode 是 4E25(100111000100101),落在第三行(0800~FFFF),因此 UTF-8 需要三个字节,格式 “1110xxxx 10xxxxxx 10xxxxxx”。从最后一个二进制位开始从前向后填 x,多出的位补 0,得到 “11100100 10111000 10100101”,十六进制即 E4B8A5。
四、Unicode 与 UTF-8 的转换
“严”的 Unicode 是 4E25,UTF-8 是 E4B8A5,二者不同,需要转换。Windows 上最直观的办法是记事本。
最下面“编码(E)”有四个选项:ANSI、Unicode、Unicode big endian、UTF-8。
-
ANSI:记事本默认。英文文件是 ASCII,简体中文是 GB2312。不同 ANSI 互不兼容,两种语言的文字不能存在同一段 ANSI 文本里。 -
Unicode:UCS-2,直接用两个字节存 Unicode 码,小头 little endian。 -
Unicode big endian:UCS-2,大头。 -
UTF-8:见上一节。
实例:在记事本输入“严”,依次选四种编码另存为,用 EditPlus 的“16 进制查看器”查看:
-
ANSI:两个字节 D1 CF,正是“严”的 GB2312。 -
Unicode:四个字节 FF FE 25 4E,其中 FF FE 表示小头,真正编码是 25 4E。 -
Unicode big endian:四个字节 FE FF 4E 25,FE FF 表示大头,真正编码是 4E 25。 -
UTF-8:六个字节 EF BB BF E4 B8 A5。前三个 EF BB BF 表示这是 UTF-8(BOM),后三个 E4B8A5 是“严”的编码,存储顺序与编码顺序一致。
五、参考文献与本系列
-
Unicode 维基百科:http://zh.wikipedia.org/wiki/Unicode -
Unicode 百度百科:http://baike.baidu.com/view/40801.htm -
字符编码笔记:ASCII,Unicode和UTF-8:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html -
UTF-8 百度百科:http://baike.baidu.com/view/25412.htm
本系列
-
java中文乱码解决之道(一)—–认识字符集 -
java中文乱码解决之道(二)—–字符编码详解:基础知识 + ASCII + GB** -
java中文乱码解决之道(三)—–编码详情:伟大的创想—Unicode编码
原文出处:chenssy
一句话总结:Unicode 给每个字符唯一码位;落地传输用 UTF。UTF-8 变长且兼容 ASCII,文件开头的 BOM(FEFF / FFFE / EF BB BF)告诉你端序和编码。
转载请注明来源:字符编码详解









