最近在做嵌入式开发,这个嵌入式平台上,支持标准c库,但不支持mbcs,也不支持unicode。里面的wchar_t被直接定义为char(typedef char wchar_t;),可见这个wchar_t是假的,只是为了让含有wchar_t的程序能通过编译,并不是支持unicode,当然也就没有对应的wcs函数族。现在要让这个系统上的程序支持中文,有下面几种想法。
首先要弄清支持中文的含义,先分析一下需求,这个程序对字符串的操作主要是如下流程,从文件中读进字符串(文件编码可自己定义),对字符串进行查找、截取、拼接操作,最后把完成的字符串作为参数传到另一个库里(暂且叫它libn吧)。需要说明的是,libn由公司别的小组实现,它不关注字符集,里面不再处理(截取、拼接)字符串,只是把输入的字符串输出到文件或屏幕,目前已经有一个比较稳定的跨平台版本。
需求确定以后,接下来要确定中文在程序中的存储编码。对中文来说,通常有三种编码方案可供选择:
1. 用mbcs编码存储(gb2312/gbk/gb18030)。
2. 用unicode编码存储。
3. 用utf-8编码存储。
这个系统里针对这三种编码的字符串函数都没有,不管采用哪种方案,字符串函数都得自己写,从这点来说,三种方案的工作量都差不多。
先来看看mbcs,这个方案纯粹是为了中文而支持中文,如果将来要兼容其他语言,mbcs函数都得重写,而且mbcs跟unicode的转换没有固定的公式,必须依赖于一张大表。用mbcs没有什么特别的好处,这个方案只能早早的就否决了。
再来看看unicode和utf-8。一般说来,unicode是国际化的终极解决方案,大部分c编译器支持wchar_t数据类型,如果编译器不支持wchar_t,可以自己使用unsigned short或unsigned int来模拟。不管什么语言,每个字符都被放到2字节的wchar_t类型里(linux下是4字节),通常对于新的程序,都推荐使用unicode。而utf-8是unicode的一种存储方案。
下面我们从不同方面来比较一下unicode和utf-8各自的优势:
1. 内存空间。unicode对于每个字符都是2个字节,utf-8对英文是一个字节,对汉字是2个或3个字节。对于英文来说,utf-8占优,但在汉字占多数的情况下,unicode占优势。当然,如果字符串的数量不是很大的话,这个问题不是很突出。这里列出来,对文件存储也可以起到一个参考作用。
2. 程序编写难度。unicode是定长类型,而utf-8是变长的,每操作一个字符的时候,都要考虑这个字符的长度,毫无疑问unicode的字符串函数编写起来应该更简单。目前,这两种字符串函数都有大量的实现可供参考,对于写程序来说,问题不大。
3. 程序执行效率。unicode定长,utf-8变长。对于strlen,substr之类的操作,unicode很方便,utf-8却要从头到尾扫描,而且需要边扫描边判断字符长度。因此unicode比utf-8要快很多,但如果这种操作不是很多,效率影响也不会特别明显。
4. 现有程序的数量。unicode程序我们见得多了,但采用utf-8的程序也不少,gtk+就是。它们都运行得很好。
5. 兼容性。英文的utf-8编码跟ascii完全一样,因此也兼容标准c库的字符串函数,如果不需要操作字符,完全不用关心语言。对于unicode,标准c库的字符串函数不能工作,字符串函数都得重写,常常用一个宏来控制在unicode和ascii直接切换(比如windows下的TCHAR)。
从上面几点来看,跟utf-8相比,unicode占据绝对优势。只有unicode的世界真美好...
但事实上,libn因为它并不关心字符集,所以它把接口的字符串类型全部声明成char*了,如果libn也用unicode实现,那就完美了,可惜,这不在我的控制范围之内。
另外还有一种方案,在我的程序内部使用unicode,在调用libn的接口处,转换成utf-8,传给libn,从libn返回的utf-8字符串,先转成unicode再使用。这个方法听起来也不错,但是很多对象并不是调用接口时才生成,也不是调用完就销毁,这样会导致我的程序内会长期存在字符串的unicode和utf-8两种拷贝,浪费大量内存,对于嵌入式系统来说,这很难容忍。
最终,我决定在我的程序内部使用utf-8编码,作出这个决定的最主要原因是因为我要使用libn,虽然这样我的程序会消耗更多的内存、需要编写冗长难懂的字符串函数、效率也会下降,但不得不这样。gtk+没有使用unicode而采用utf-8,恐怕也是这样妥协的结果吧。
后记:utf-8函数参考了glib中的实现。