理解Unicode编码规范在VC编程中的应用
一、字符编码基础知识
1. ASCII码
是由美国制定的一套字符编码,对英语字符与二进制位之间的关系做了统一规定,一直沿用至今。ASCII码一共规定了128个字符的编码。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。
2. 中文字符编码 GB2312、GBK、GB18030、BIG5
GB2312 码是中华人民共和国国家汉字信息交换用编码,全称《信息交换用汉字编码字符集——基本集》,由国家标准总局发布,1981年5月1日实施,通行于大陆。新加坡等地也使用此编码。GB2312 收录简化汉字及符号、字母、日文假名等共 7445 个图形字符,其中汉字占 6763 个。GB2312 规定“对任意一个图形字符都采用两个字节表示,每个字节均采用七位编码表示”,习惯上称第一个字节为“高字节”,第二个字节为“低字节”。GB2312最多能表示 6763 个汉字。GB2312 的编码范围为 2121H-777EH,与 ASCII 有重叠,通行方法是将 GB 码两个字节的最高位置 1 以示区别。
GB2312 将代码表分为 94 个区,对应第一字节;每个区 94 个位,对应第二字节,两个字节的值分别为区号值和位号值加 32(2OH),因此也称为区位码。
01-09 区为符号、数字区,
16-87 区为汉字区,
10-15 区、
88-94 区是有待进一步标准化的空白区。
GB2312 将收录的汉字分成两级:
第一级是常用汉字计 3755 个,置于 16-55 区,按汉语拼音字母/笔形顺序排列;
第二级汉字是次常用汉字计 3008 个,置于 56-87 区,按部首/笔画顺序排列。
GBK
全国信息技术化技术委员会于1995年12月1日推出《汉字内码扩展规范》。GBK 向下与 GB2312 完全兼容,向上支持 ISO 10646 国际标准。GBK 亦采用双字节表示,总体编码范围为 8140-FEFE 之间,首字节在 81-FE 之间,尾字节在 40-FE 之间,剔除 XX7F 一条线。
GBK 共收入 21886 个汉字和图形符号,包括:
* GB2312 中的全部汉字、非汉字符号。
* BIG5 中的全部汉字。
* 与 ISO 10646 相应的国家标准 GB13000 中的其它 CJK 汉字,以上合计 20902 个汉字。
* 其它汉字、部首、符号,共计 984 个。
GBK 编码区分三部分:
* 汉字区,包括:
•GBK/2:OXBOA1-F7FE, 收录 GB2312 汉字 6763 个,按原序排列;
•GBK/3:OX8140-AOFE,收录 CJK 汉字 6080 个;
•GBK/4:OXAA40-FEAO,收录 CJK 汉字和增补的汉字 8160 个。
* 图形符号区,包括:
•GBK/1:OXA1A1-A9FE,除 GB2312 的符号外,还增补了其它符号
•GBK/5:OXA840-A9AO,扩除非汉字区。
* 用户自定义区:即 GBK 区域中的空白区,用户可以自己定义字符。
GB18030
GB18030 是最新的汉字编码字符集国家标准, 向下兼容 GBK 和 GB2312 标准。 GB18030 编码是一二四字节变长编码。一字节部分从 0x0~0x7F 与 ASCII 编码兼容。 二字节部分, 首字节从 0x81~0xFE, 尾字节从 0x40~0x7E 以及 0x80~0xFE, 与 GBK 标准基本兼容。 四字节部分, 第一字节从 0x81~0xFE, 第二字节从 0x30~0x39, 第三和第四字节的范围和前两个字节分别相同。 四字节部分覆盖了从 0x0080 开始, 除去二字节部分已经覆盖的所有 Unicode 3.1 码位。
3. Unicode
Unicode是世界通用的编码规范,是对世界各国使用的符号进行唯一编码的规范。其规模可以容纳100多万个符号。注意Unicode只是个规范,一个可不断扩充的大字符集。具体应用时还受操作系统的限制,还要考虑效率和存储空间等因素。
4.UTF-8编码
UTF-8是Unicode编码规范实现方式之一。它是一种变长的编码方式,用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
可简单理解为Unicode编码的压缩编码方式。在传输、存储包含大量单字节数据时具有明显的优势。
UTF-8的编码规则很简单,只有二条:
* 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
* 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
UTF-8编码规则,字母x表示可用编码的位。
(十六进制) | (二进制)
--------------------+---------------------------------------------
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
5.UTF-16编码
UTF-16是Unicode编码规范实现方式之一。它是一种变长的编码方式,用2、4个字节表示一个符号。
UTF-16编码的规则是:
* Unicode值小于0x10000的用等于该值的16位整数来表示。
* Unicode值介于0x10000和0x10FFFF之间的,用一个值介于0xD800和0xDBFF(在所谓的高8位区)的16位整数和值介于0xDC00和0xDFFF(在所谓的低8位区)的16位整数来表示。
* Unicode值大于0x10FFFF不能按照UTF-16进行编码。
注意:在0xD800和0xDFFF间的值是特别为UTF-16预留,所以不应该将任何字符的值指定为这个区间内的数值。
将某个字符的Unicode值转换为UTF-16的编码按照以下步骤进行。假设U是给Unicode值,小于x10FFFF。
1) 如果U < 0x10000,U的编码就是无符号的十六位整数,值和其本身的值一样,处理结束。
2) 如果U等于或者小于0x10FFFF,则设U' = U - 0x10000。此时,U'一定小于或者等于0xFFFFF,也就是说,U'的值不会超过20位。
3) 分别初始化2个16位无符号的整数,W1和W2为0xD800和0xDC00。每个整数都有10位可以用来对字符进行编码,正好能容纳U'的20位。
4) 将U'的高10位分配给W1的低10位,将U'的低10位分配给W2的低10位,处理结束。
用数字来表示,第2步到第4步如下所示:
U' = yyyyyyyyyyxxxxxxxxxx
W1 = 110110yyyyyyyyyy
W2 = 110111xxxxxxxxxx
5.UTF-32编码 用四字节实现Unicode编码规范方式。
二、理解Unicode编码规范在VC编程中的应用
Unicode字符编码规范在应用上有多种实现方式,如:UTF-8,UTF-16,UTF-32等,每种方式的特性决定了它的用途。系统字库就是它的一个应用实例。
谈Unicode字符编码规范的应用离不开操作系统,以WINDOWS为例。系统表示字符的方法经历了从单字节ASCII、多字节MBCS(包含单、双字节字符串)、Unicode(双字节,可扩展成二、四字节)的过程。以后系统会发展到全面支持四字节Unicode字符。
windows XP是当前使用最多的操作系统。系统所带的字符限制在Unicode编码规范中UTF-16能用二字节表示的区域内,windows XP字符集只包含了6万多字符,因此系统是用二字节UTF-16编码表示一个字符,UTF-16编码是以小头方式(Little endian)存放,即第二字节放在前面。目前我们在VC编程和使用WINXP系统时遇到"Unicode"这个单词时,通常是指用UTF-16编码的二字节字符格式。windows XP同时还支持MBCS编码字符,当调用winapi时系统会自动将MBCS转换成UTF-16后再处理。
UTF-16二、四字节编码字符的特性为等宽二字节编码字符到等宽四字节编码字符提供了过渡时期的解决方案。字符串中的二、四字节字符很容易被区分出来,因此系统在扩展后能有限支持四字节字符。安装GB18030扩展包,它提供了一个宋体-18030字体和几个处理四字节字符的函数,其它就交给程序员了,真要在程序中处理四字节字符并非易事。
UTF-8:主要用于传输、存储包含大量单字节的数据。
三、常用编码转换
MBCS -> Unicode -> UTF8
UTF8 -> Unicode -> MBCS
WideCharToMultiByte
MultiByteToWideChar
static void UTF_8ToUnicode(wchar_t* pOut,char *pText); // 把UTF-8转换成Unicode
static void UnicodeToUTF_8(char* pOut,wchar_t* pText); //Unicode 转换成UTF-8
static void UnicodeToGB2312(char* pOut,wchar_t uData); // 把Unicode 转换成 GB2312
static void Gb2312ToUnicode(wchar_t* pOut,char *gbBuffer);// GB2312 转换成 Unicode
static void GB2312ToUTF_8(string& pOut,char *pText, int pLen);//GB2312 转为 UTF-8
static void UTF_8ToGB2312(string &pOut, char *pText, int pLen);//UTF-8 转为 GB2312
static void UTF_8ToUnicode(wchar_t* pOut,char *pText); // 把UTF-8转换成Unicode
static void UnicodeToUTF_8(char* pOut,wchar_t* pText); //Unicode 转换成UTF-8
static void UnicodeToGB2312(char* pOut,wchar_t uData); // 把Unicode 转换成 GB2312
static void Gb2312ToUnicode(wchar_t* pOut,char *gbBuffer);// GB2312 转换成 Unicode
static void GB2312ToUTF_8(string& pOut,char *pText, int pLen);//GB2312 转为 UTF-8
static void UTF_8ToGB2312(string &pOut, char *pText, int pLen);//UTF-8 转为 GB2312
以上基础知识部分摘自网友们的博客