Unicode和中文字符处理

编码方式

UTF8
单字节(byte)。目前使用1~4字节,变长编码,可以任意扩充,参考 RFC3629。 UTF16
双字节(word)。超过U+10000的编码以4字节编码。 UTF32
四字节。

Qt QString 和 Java String 内部是UTF16表示。UTF16有字节序要求,UTF8没有。

UTF-8编码

Unicode内码范围(HEX) UTF8串(BIN)
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

这里是一些常见字符的内码,更多可以参考Unicode内码表

字符 Unicode内码范围
0-9 0x30-0x39
A-Z 0x41-0x5A
a-z 0x61-0x7A
Latin标点 0x21-0x2F 0x3A-0x40 0x5B-0x60 0x7B-0x7E
一般标点 0x2000-0x206F
CJK标点 0x3000-0x303F
CJK字符 0x4E00-0x9FA5
全角字符 0xFF00-0xFFEF

可见中文所属的CJK,最常用的内码范围是 0x4E00-0x9FA5,在0x0800-0xFFFF之间,所以会用三字节进行编码,补上一个1110,两个10,UCS-2中两字节的中文就变成了UTF-8的三字节。

以“中”字为例,Unicode内码0x4E2D的二进制是:

0100 1110 0010 1101

对照UTF-8模板:

1110xxxx 10xxxxxx 10xxxxxx

对号入座得到:

11100100 10111000 10101101

所以“中”字的UTF-8编码为:E4 B8 AD

参考 U+4E00-U+9FFF码表这里可以根据 Unicode 码查到所有的字符。

同时,也可以在正则表达式中使用 [\u4e00-\u9fa5] 来简单的判断中文字符。

编程处理

C++

C++中如果要处理中文字符,建议使用std::wstring,这样每个字都是wchar_t固定双字节长度,比较好操作。不过必须要设置locale:

std::setlocale(LC_ALL, "en_US.utf8");

把UTF8存储的std::string转换为std::wstring可以使用标准库的 std::mbstowcs()std::wcstombs() 函数,具体可以参考标准库转换函数

Java

Java中的char是双字节,只能表达UTF16中的BMP部分字符。

BMP:U+0000~U+D7FF, U+E000~U+FFFF

【注】有人实验说char内部编码为UTF16,和 Charset.defaultCharset() 没有关系。

如果想遍历出String里所有中英文字符,一般简单来说调用 String.toCharArray() 遍历char数组即可,风险是BMP外的Unicode字符会出错。

Python

如果已知字符的unicode编码但不清楚字符,或者想输出某已知字符的unicode编码,可以借助python预览一下。先确定你终端的locale是en_US.UTF-8等unicode兼容环境。

>>> print u"\u4e00"
一
>>> "一".decode("utf8")
u'\u4e00'
>>> hex(ord(u'\u4e00'))
'0x4e00'