彻底解密C++宽字符:6、国际化策略(完)

来源:互联网 发布:the essence of sql 编辑:程序博客网 时间:2024/06/06 06:53


硬编码的硬伤

我们现在知道,C/C++的宽窄转换是依赖系统的locale的,并且在运行时完成。考虑这样一种情况,我们在简体中文Windows下编译如下语句:
const char* s = "中文abc";
根据我们之前的讨论,编译器将按照Windows Codepage936(GB2312)对这个字符串进行编码。如果我们在程序中运行宽窄转换函数,将s转换为宽字符串ws,如果这个程序运行在简体中文环境下是没问题的,将执行从GB2312到UCS-2BE的转换;但是,如果在其他语言环境下,比如是繁体中文BIG5,程序将根据系统的locale执行从BIG5到UCS-2BE的转换,这显然就出现了错误。

补救

有没有补救这个问题的办法呢?一个解决方案就是执行不依赖locale的宽窄转换。实际上,这就已经不是宽窄转换之间的问题了,而是编码之间转换的问题了。我们可以用GNU的libiconv实现任意编码间的转换,对于以上的具体情况,指明是从GB2312到UCS-2BE就不会出错。(请参考本人前面的章节:win32下的libiconv),但这显然是一个笨拙的策略:我们在简体中文Windows下必须使用GB2312到UCS-2BE版本的宽窄转换函数;到了BIG5环境下,就必须重新写从BIG5到UCS-2BE的宽窄转换函数。

Windows的策略

Windows的策略是淘汰了窄字符串,干脆只用宽字符串。所有的硬编码全部加上特定宏,比如TEXT(),如果程序是所谓Unicode编译,在编译时就翻译为UCS2-BE——Windows自称为Unicode编程,其本质是使用了UCS-2BE的16位宽字符串。

Linux的策略

Linux下根本就不存在这个问题!因为各种语言的Linux都使用UTF-8的编码,所以,无论系统locale如何变化,窄到宽转换的规则一直是UTF-8到UTF32-BE 。

跨平台策略

因为在16位的范围内,UTF32-BE的前16位为0,后16位与UCS2-BE是一样的,所以,即使wchar_t的sizeof()不一样,在一般情况下,跨平台使用宽字符(串)也应该是兼容的。但是依然存在潜在的问题,就是那些4字节的UTF32编码。

gettext策略

以上都是将ASCII及以外的编码硬编码在程序中的办法。GNU的gettext提供了另外一种选择:在程序中只硬编码ASCII,多语言支持由gettext函数库在运行时加载。(对gettext的介绍请参考本人前面的章节:Win32下的GetText)。gettext的多语言翻译文件不在程序中,而是单独的提出来放在特定的位置。gettext明确的知道这些翻译文件的编码,所以可以准确的告诉给系统翻译的正确信息,而系统将这些信息以当前的系统locale编码成窄字符串反馈给程序。例如,在简体中文Windows中,gettext的po文件也可以以UTF-8储存,gettext将po文件翻译成mo文件,确保mo文件在任何系统和语言环境下都能够正确翻译。在运行是传给win32程序的窄串符合当前locale,是GB2312。gettext让国际化的翻译更加的方便,缺点是目前我没找到支持宽字符串的版本(据说是有ugettext()支持宽字符串),所以要使用gettext只能使用窄字符串。但是gettext可以转换到宽字符串,而且不会出现宽窄转换的问题,因为gettext是运行时根据locale翻译的。例如:
const char* s = gettext("Chinese a b c");
其中"Chinese a b c"在po中的翻译是"中文abc"
使用依赖locale的运行时宽窄转换函数:
const std::wstring wstr = s2ws(s);
运行时调用该po文件对应的mo文件,在简体中文环境下就以GB2312传给程序,在繁体中文中就以BIG5传给程序,这样s2ws()总能够正常换算编码。

更多

在本文的最后,我想回到C++的stream问题上。用fstream转换如此的简单,sstream却不支持。改造一个支持codecvt的string stream需要改造basic_stringbuf。basic_stringbuf和basic_filebuf都派生自basic_streambuf,所不同的是basic_filebuf在构造和open()的时候调用了codecvt,只需要在basic_stringbuf中添加这个功能就可以了。说起来容易,实际上是需要重新改造一个STL模板,尽管这些模板源代码都是在标准库头文件中现成的,但是我还是水平有限,没有去深究了。另外一个思路是构建一个基于内存映射的虚拟文件,这个框架在boost的iostreams库中,有兴趣的朋友可以深入的研究。
(完)