万恶的字符集

UTF8

日常生活中经常要与编码打交道,老是分不清各种编码的区别,一怒之下,就有了这篇文章。

1. ASCII 码

八位,一个字节,最高位为0,表示全部英文字符。最先出现的字符集,全称是 American Standard Code for Information Interchange ,从名字就看出来这是只适用于美国人的,于是乎其他国家也先后对 ASCII 码进行扩展,以表示自己的语言字符。

2. 对 ASCII 码的扩展

中国人对 ASCII 码的中文扩展自然就是:GB2312、GB18030(后者是前者的扩充),统称为DBCS(Double Byte Charecter Set 双字节字符集) ,这个字符集标准就是,两字节长的汉子字符和一字节长的英文字符并存于同一套编码方案中,意思就是:在这个字符集标准里,当一个字节的值大于 127 (把大于 127 的值的字符都全部干掉,重新编码了)的话,那就认为它是一个中文字符,一句话来说,就是 一个汉字算两个英文字符

但是这样子每个国家都有自己的编码标准,在互联网时代,势必会在相互通信上造成很大烦扰。于是就有了一个统一天下的字符集标准:Unicode。

3. Unicode

Unicode 顾名思义就是能够表示几乎所有字符的编码字符集,注意这里只是一个字符集,它规定了每个符号都拥有自己独一无二的编码。

但是这只是一个符号集,只规定了符号的编码,而没有规定计算机该如何存储这个编码。

假设一个汉字的 Unicode 二进制编码有 15 位,即至少需要 2 个字节来表示。而同时存在一个英文字符,它的二进制编码只有 8 位,只需要一个字节就可以表示了。那计算机该怎么知道 2 个字节代表着一个汉字,而不是分别代表 2 个英文字符呢?如果 Unicode 统一规定所有字符都用 3 个字节来表示,没有用到的全置 0 ,那这样子对英文字符来说,势必是巨大的浪费。

于是乎,我们需要一个科学的 Unicode 字符集编码规范,那就是我们常说的 UTF-8了。

4. UTF-8

UTF-8 是 Unicode 的实现方式之一,还有 UTF-16(字符用两个字节或四个字节表示)、UTF-32(字符用四个字节表示),它们与 Unicode 的关系是:

  • Unicode 是编码字符集,每个字符在这里都能找到一个二进制编码;
  • UTF-X 是字符编码,规定计算机该以什么形式( 1 个字节? 2 个字节?)来存储 Unicode 二进制编码。

UTF-8 的编码规则

这里讲的很清晰了,所以我就直接引用他的内容了:

UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用 1~4 个字节表示一个符号,根据不同的符号而变化字节长度。

UTF-8 的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

2)对于 n 字节的符号(n>1),第一个字节的前 n 位都设为 1,第 n+1 位设为 0,后面字节的前两位一律设为 10。剩下的没有提及的二进制位,全部为这个符号的 unicode 码。

下表总结了编码规则,字母 x 表示可用编码的位。

1
2
3
4
5
6
7
8
> Unicode 符号范围 | UTF-8 编码方式
> (十六进制) | (二进制)
> --------------------+---------------------------------------------
> 0000 0000-0000 007F | 0xxxxxxx
> 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
> 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
> 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
>

>

跟据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是 0,则这个字节单独就是一个字符;如果第一位是 1,则连续有多少个 1,就表示当前字符占用多少个字节。

下面,还是以汉字 “严” 为例,演示如何实现 UTF-8 编码。

已知 “严” 的 unicode 是 4E25(100111000100101),根据上表,可以发现 4E25 处在第三行的范围内(0000 0800-0000 FFFF),因此 “严” 的 UTF-8 编码需要三个字节,即格式是 “1110xxxx 10xxxxxx 10xxxxxx”。然后,从 “严” 的最后一个二进制位开始,依次从后向前填入格式中的 x,多出的位补 0。这样就得到了,”严” 的 UTF-8 编码是 “11100100 10111000 10100101”,转换成十六进制就是 E4B8A5。

5. 文本文件的编码

当你在网络中传输一个文件时,你需要告诉接收方,这个文件该以什么样的编码方式打开,不然你用 UTF-8 方式进行编码,然后对方用 ANSI 方式解码,到最后就是一堆乱码啦~

假设你的文本文件里只有一个汉字 “严” ,它的 Unicode 编码是 254E ,UTF-8 编码是 E4B8A5 ,GB2312 编码是 D1CF

如果你以下列各种方式进行编码的话,文本文件的编码都会略有不同:(建议用 UltraEdit 软件进行查看,以十六进制的格式)

  1. ANSI: 该编码方式,对于英文文件就采用 ASCII 编码方式,对于中文就用 GB2312。此时这个文件的编码就是两个字节 D1 CF
  2. Unicode: 指的是 UCS-2 编码方式,以两个字节存入字符的Unicode 码。此时这个文件的编码就是四个字节 FF FE 25 4E,其中FF FE表示的是以小头方式存储。
  3. Unicode big endian: 此时这个文件的编码就是四个字节 FE FF 25 4E,其中FE FF表示的是以大头方式存储。
  4. UTF-8: 文件的编码就是六个字节 EF BB BF E4 B8 A5 ,前三个字节EF BB BF代表了这是 UTF-8 编码。

6. 引用

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

http://cenalulu.github.io/linux/character-encoding/

http://blog.csdn.net/ylyuanlu/article/details/41844009