1. Character sets

最熟悉也最简单的字符集莫过于ASCII,如小写字母a对应的编码是97。ASCII采用7位来编码,Extend-ASCII采用8位来编码,所以分别只用表示127和255个字符。如果要编码更多的字符当然需要更多的位来实现,因此需要制定不同的编码来表示不同的语言,如果中文的GB2312。Windows的字符编码称为Code pages,所以经常看GB2312也经常表示为CP936,也有的称为WINDOWS-936。

不同的字符集的编码范围及显示都不一样,一般都只能特定的语言,所以如果程序(如浏览器)对文本采用了不同的字符编码时,就会出现所谓的乱码。Windows下默认的编码显示为ANSI, 事实上ANSI并不是一个具体的字符编码。无论是英文版、中文版、日文版Windows,默认编码都是ANSI,但实际上对应不同的编码:

Note ANSI code pages can be different on different computers, or can be changed for a single computer, leading to data corruption. For the most consistent results, applications should use Unicode, such as UTF-8 or UTF-16, instead of a specific code page.

示例

在Windows下创建一个文件cn.txt,输入中文并采用Windows默认编码,然后在Linux用cat查看,将看到中文显示为乱码,因为Linux shell默认编码是UTF-8, 用file命令查看cn.txt:

file cn.txt

显示结果为:

cn.txt: ISO-8859 text, with CRLF line terminators

或者

file -bi cn.txt

结果为:

text/plain; charset=iso-8859-1

事实上结果是错误的,如果试图用iconv转换为UTF-8:

iconv -f ISO-8859-1 -t UTF-8 cn.txt -o cn2.txt

cn2.txt中文依然是乱码。

由于cn.txt是在中文Windows下创建,其编码实际上是GB2312,所以需要将-f参数改为GB2312/WINSOWS-936/CP936/GBK,所以这样才能正确转换:

iconv -f WINDOWS-936 -t UTF-8 cn.txt -o cn2.txt

再用file查看,发现除了显示为UTF-8编码外,还显示换行符是CRLR,这是windows的默认换行符:

cn2.txt: UTF-8 Unicode text, with CRLF line terminators

2. Unicode

由于国际化的需要,很多内容需要同时表示多种语言的字符,因此需要一种统一的字符编码,Unicode就是为此而诞生。

Unicode provides a unique number for every character, no matter what the platform, no matter what the program, no matter what the language.

Unicode定义了几乎所有文字的字符(还包括一此没有使用的)编码及其表现(representation),以及Unicode的实现方式UTF-8、UTF-16、UTF-32。

Unicode将中文、日文、韩文的编码统称为CJK Unified Ideographs,编码范围为U+4E00-U+9FCC,可以在这里查询汉字的Unicode编码。

3. UTF-8

UTF-8也许是目前应用最广的Unicode实现方式,其编码规则如下:

编码范围 二进制序列
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兼容ASCII字符编码;从CJK的编码范围可知,中文编码是三个字节,“中”的Unicode编码为U+4E2D,故其UTF-8表示是0xE4B8AD。

示例

创建utf8.txt并输入以下内容:

a中国

用xxd命令查看其16进制内容:

xxd utf8.txt

输入如下:

61e4 b8ad e59b bd0a

0x61是小写字母a, 0x0A是换行符:

0x61 = 01100001 = 97(a)
0x0A = 00001010 = 10(new line)
0xE4B8AD = 1110 0100 10111000 10101101 => 0100 1110 0010 1101 = 4E2D(中)
0xE59BBD = 1110 0101 10011011 10111101 => 0101 0110 1111 1101 = 56FD(国)

Work with Regular expression

在正则中有时需要匹配中文字符,不同语言表示有点不同:

JavaScript示例:

var str = 'abc中国def';
str.match('\u4e2d')
str.match('中')

PHP示例:

$str = 'abc中国def';
preg_match('/中/', $str, $match);//UTF-8 file
preg_match('/\x{4e2d}/u', $str, $match);

4. Conclusion

GB2312/GBK是用两个字节编码中文,而UTF-8需要3个字符,所以不需要考虑国际化因素,使用GB2312可以节约一点空间,对于网页来说可以减少传输内容,节约一点带宽资源。但在国际化的大环境中,UTF-8还是首选。

5. Referecne

  1. Windows Code page identifiers.
  2. Character encoding(Wikipedia).
  3. RFC3629, UTF-8.
  4. Unicode official site.
  5. Unicode 6.1.0.
  6. Unicode 6.1 Character Code Charts.