waterflier's Blog

热爱生活,热爱编程
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

跨平台程序的UNICODE字符串处理方法。

Posted on 2005-11-10 11:44  waterflier  阅读(1748)  评论(0编辑  收藏  举报

在windows下面编程,我们通常都知道unicode这个概念,如果一个程序是unicode的,那么他将调用

unicode的api。这个时候,所有传递给api的字符串参数都要是unicode的.如果使用C的风格,是很简单的

,字符串全部由char* str 转变成TCHAR* str,使用的crt函数(其实也是api)时调用_tcslen类的函数族就

可以了。

在讨论其它问题前要先明确一个概念:unicode 与 utf-8编码,utf-16编码是两个不同类别的术语。

unicode对一个字符提供了一个唯一的编码(参看下面的资料,关于UCS-2与UCS-4)
假设"中"这个字符的编码是0x34 0x34(我乱写的),utf-8对其编码,得到的是 0xE3 0x90 0xB6 需要3byte

的空间进行存储。不同的unicode码经过utf-8编码后会得到变长的结果.比如说'a'经过utf-8编码后得到

的是和ascii码相同,只占1个byte.对unicode使用不同的方法编码,可以有效的节约存储空间(如果选择了

错误的编码,会浪费空间).

中的unicode(UCS-2)值和编码后的结果
unicode                            utf-8          
00110100 00110100      11100011  10010000 10110110
a的unicode值和编码后的结果
unicode                            utf-8
00000000 01100001      01100001

这里得出的结论是utf-8,utf-16编码是在存储字符串信息前的一个选择,而不是处理字符串的选择.


OK,问题回到字符串编程上来。第一个问题,就是要选择内存中的字符串格式,包括自己所有需要传递字

符串参数的函数的参数定义。我们这里有3个选择,char*(ASCII string),unsigned short*(UCS2

string,windows下的unicode),unsigned long*(UCS4 string,真正的unicode支持)。windows下的api是不

支持UCS4的,所以在windows平台下最好只做前2个的选择。类似于windows的TCHAR定义,我们可以做这样

的定义
#ifdef _UCS2
#define TCHAR unsigned short
#else
#ifdef _UCS4
#define TCHAR unsigned long
#else
#define TCHAR char
#endif
#endif
然后有一个问题,如果在程序中需要使用一个预定义的字符串,比如说
TCHAR* str = "中国";
那么,str指向的常量字符串的编码是ACSII string,还是UCS2 unicode string,还是UCS4 unicode

string,是取决于编译器的。这样就容易造成许多不易发现的错误。这里要推荐一个string table的概念

,用如下代码替换。
const TCHAR* str = StringTable::LoadStr(ID_HOMELOAD_NAME);
StringTable类解析一个指定编码的本地字符串表XML文件(可以用各种编码存储),这个文件可以使用自定

义的工具或则是各种XML编辑工具来生成。使用StringTable::SetOutPutType(enum MemStrType)来使之在

LoadStr的时候转成各种字符串编码。当然,这个类中定义了一系列的编码转换函数,比如说

UTF8TOASCII,UTF8TOUCS2,UTF8TOUCS4,UCS4TOUCS2,UCS4TOASCII,UCS4TOXXX,StringTalbe内部使用UCS4作

为读取后的字符串存储格式,然后再根据StringTable::SetOutPutType指定的输出类型生成相应编码的

Table.这样做的好处就是把这个编码的问题重视化,即时出现编码不一致的错误,也能立刻修正。

在linux下,系统对UCS4的支持比较好,#include<wchar.h>,里面的函数的接口都是ucs4 string.所以如果写跨平台程序,肯定是要用ucs2的(UCS4windows不支持,而且可以节约内存,但是你的程序就不是真正的UNICODE3.1 Support了,而且也不能支持国家标准GB18030).然后再调用linux的相关函数时,转化为UCS4.参考文章http://www0.ccidnet.com/tech/os/2001/07/31/58_2811.html。我懒得写了。

 


 -----------------------------------
p.s 我摘得一些相关术语的定义
Unicode 的定义
Unicode 通常用作涉及双字节字符编码方案的通用术语。Unicode CCS 3.1 的官方称谓是 ISO10646-1 通

用多八字节编码字符集(Universal Multiple Octet Coded Character Set,UCS)。Unicode 3.1 版本

添加了 44,946 个新的编码字符。算上 Unicode 3.0 版本已经存在的 49,194 个字符,共计 94,140 个

Unicode 编码字符集利用了一个由 128 个三维的组构成的四维编码空间。其中每个组包含 256 个二维平

面。每个平面由 256 个一维的行组成,并且每个行有 256 个单元。每个单元在这个编码空间内对一个字

符编码,或者被声明为未经使用。这种编码概念被称为 UCS-4;四个八位元用来表示指定组、平面、行和

单元的每个字符。

第一个平面(第 00 组的第 00 平面)是基本多语言平面(Basic Multilingual Plane,BMP)。BMP 按

字母、音节、表意符号和各种符号及数字定义了常规使用的字符。后续的平面用于附加字符或其它还没有

发明的编码实体。我们需要这完整的范围去处理世界上的所有语言;特别是拥有将近 64,000 个字符的一

些东亚语言。

BMP 被用作双字节的编码字符集,这种编码字符集确定为 ISO 10646 UCS-2 格式。ISO 10646 UCS-2 就

是指 Unicode(并且两者相同)。BMP,像所有 UCS 平面那样,包含了 256 行,其中每行包含 256 个单

元,字符仅仅按照 BMP 中的行和单元的八位元在单元中被编码。 这就允许 16 位编码字符能够被用来书

写大多数商业上最重要的语言。UCS-2 不需要代码页切换、代码扩展或代码状态。UCS-2 是一种将

Unicode 结合到软件中的简单方法,但它只限于支持 Unicode BMP。

若要用 8 位字节表示一个多于 2^8 =256 个字符的字符编码系统(character coding system,CCS),

就需要一种字符编码方案(character-encoding scheme,CES)。


UTF-8
UTF-8 转换格式正逐步成为一种占主导地位的交换国际文本信息的方法,因为它可以支持世界上所有的语

言,而且它还与 ASCII 兼容。UTF-8 使用变长编码。从 0 到 0x7f(127)的字符把自身编码成单字节,

而将值更大的字符编码成 2 到 6 个字节。

表 1. UTF-8 编码
0x00000000 - 0x0000007F:  0 xxxxxxx 
0x00000080 - 0x000007FF:  110 xxxxx10 xxxxxx 
0x00000800 - 0x0000FFFF:  1110 xxxx10 xxxxxx10 xxxxxx 
0x00010000 - 0x001FFFFF:  11110 xxx10 xxxxxx10 xxxxxx 10 xxxxxx 
0x00200000 - 0x03FFFFFF:  111110 xx10 xxxxxx10 xxxxxx10 xxxxxx 10 xxxxxx 
0x04000000 - 0x7FFFFFFF:  1111110 x10 xxxxxx10 xxxxxx10 xxxxxx 10 xxxxxx10 xxxxxx