文章地址Q?http://kittsoft.xp3.biz/?p=86
“q个问题比你惌中复?#8221;
Q我也学下BS的风|虽然q句话是我自׃时想说的。^^Q?br />从字W到整数
char是一U整数类型,q句话的含义是,char所能表C的字符在C/C++中都是整数类型。好Q接下来Q很多文章就会DZ个典型例子,比如Q?#8217;a' 的数值就?×61。这U说法对吗?如果你细心的读过K&R和BS对于C和C++描述的原著,你就会马上反驳道Q?×61只是’a'的ASCII |q没有Q何规定C/C++的char值必d应ASCII。C/C++甚至没有规定char占几位,只是规定了sizeof(char){于1?br />当然Q目前大部分情况下,char?位的Qƈ且,在ASCII范围内的|与ASCII对应?br />本地化策略集QlocaleQ?br />“?#8217;a'译?×61的整数?#8221;Q?#8220;ASCII范围内的~码与char的整数值对应v?#8221;Q类DL规定Q是特定pȝ和特定编译器制定 的,C/C++中有个特定的名词来描q这U规定的集合Q本地化{略集(locale。也有翻译成“现场”Q。而翻?#8212;—也就是代码{换(codecvtQ? 只是q个集合中的一个,C++中定义ؓ{略Qfacet。也有翻译ؓ“刻面”Q?br />C/C++的编译策?br />“本地化策略集”是个很好的概念,可惜在字W和字符串这个层面上QC/C++q不使用QC++的locale通常只是影响(streamQ)QC/C++使用更直接简单的{略Q硬~码?br />单的_字符Q串Q在E序文gQ可执行文gQ非源文Ӟ中的表示Q与在程序执行中在内存中的表CZ致。考虑两种情况Q?br />A、char c = 0×61;
B、char c = ‘a’;
情况A下,~译器可以直接认识作为整数的cQ但是在情况B下,~译器必d’a'译成整数。编译器的策略也很简单,是直接d字符Q串Q在源文件中的编码数倹{比如:
const char* s = “中文abc”;
q段字符串在GB2312QWindows 936Q,也就是我们的windows默认中文pȝ源文件中的编码ؓQ?br />0xD6 0xD0 0xCE 0xC4 0×61 0×62 0×63
在UTF-8Q也是Linux默认pȝ源文件中的编码ؓQ?br />0xE4 0xB8 0xAD 0xE6 0×96 0×87 0×61 0×62 0×63
一般情况下Q编译器会忠实于源文件的~码为s赋|例外的情冉|如VC会自作聪明的把大部分其他cd~码的字W串转换成GB2312Q除了像UTF-8 without signatureq样的幸存者)?br />E序在执行的时候,s也就保持是这L~码Q不会再做其他的转换?br />宽字W? wchar_t
正如char没有规定大小Qwchar_t同样没有标准限定Q标准只是要求一个wchar_t可以表示Mpȝ所能认识的字符Q在win32 中,wchar_t?6位;Linux中是32位。wchar_t同样没有规定~码Q因为Unicode的概忉|们后面才解释Q所以这里只是提一下,? win32中,wchar_t的编码是UCS-2BEQ而Linux中是UTF-32BEQ等价于UCS-4BEQ,不过单的_?6位以内,一个字 W的q?U编码值是一L。因此:
const wchar_t* ws = L”中文abc”;
的编码分别ؓQ?br />0x4E2D 0×6587 0×0061 0×0062 0×0063 //win32Q?6?br />0x00004E2D 0×00006587 0×00000061 0×00000062 0×00000063 //LinuxQ?2?br />大写的L是告诉编译器Q这是宽字符丌Ӏ所以,q时候是需要编译器Ҏlocale来进行翻译的?br />比如Q在Windows环境中,~译器的译{略是GB2312到UCS-2BEQLinux环境中的{略是UTF-8到UTF-32BE?br />q时候就要求源文件的~码与编译器的本地化{略集中代码译的策略一_例如VC只能dGB2312的源代码Q这里还是例外,VC太自作聪明了 Q会很多其他代码在~译时自动{换成GB2312Q,而gcc只能dUTF-8的源代码Q这里就有个尬QMinGWq行win32下,所以只? GB2312pȝ才认Q而MinGW却用gcc~写Q所以自己只认UTF-8Q所以结果就是,MinGW的宽字符被废掉了Q?br />宽字W(Ԍq译器译Q还是被编码进E序文g中?/p>
Unicode和UCS
Unicode和UCS是两个独立的l织分别制定的一套编码标准,但是因ؓ历史的原因,q两套标准是完全一L。Unicodeq个词用得比较多的原因可 能是因ؓ比较ҎCQ如果没有特别的声明Q在本文所提及的Unicode和UCS是一个意思。Unicode的目标是建立一套可以包含hcL有语a? 字符号你惛_到想不到的各U东西的~码Q其~码定w甚至预留了火星语以及银河pM外语a的空?#8212;—开个玩W,反正单的_Unicode~码集够的 大,如果用计机单位来表C,其数量比3个字节大一些,不到4个字节?br />Unicode和UTF
因ؓUnicode包含的内容太多,其编码在计算Z的表C方法就成ؓ了一个有必要研究的问题。传l编码,比如标准?位ASCIIQ在计算Z的表C方 法就是占一个字节的?位,q似乎是不需要解释就W合大家习惯的表C方法。但是当今Unicode的L辑ֈ32位(计算机的最单位是字节Q所以大? 字节Q就只能臛_?字节表示Q,对于大部分常用字W,比如Unicode~码只占一个字节大的p字母Q占两个字节大小汉字Q都?个字节来储存太奢 侈了。另外,如果都用4字节直接表示Q就不可避免的出Cؓ0的字节。而我们知道,在C语言中,0×00的字节就?#8217;\0′Q表C的是一个字W串Qchar 字符Ԍ非wchar_tQ的l束Q换句话_C风格的char字符串无法表CUnicode?br />因ؓcM的种U问题,为Unicode在计机中的~码Ҏ出现了,q就是UTFQ所对应的,为UCS~码实现的方式也有自q说法。一般来_UTF- xQx表示q套~码一个单位至占用x位,因ؓUnicode最长达?2位,所以UTF-x通常是变长的——除了UTF-32Q而UCS-y表示一个单 位就占用y个字节,所以能表示当今Unicode的UCS-y只有UCS-4Q但是因为历史的原因Q当Unicodeq没那么庞大的时候,2个字节够表 C,所以有UCS-2Q现在看来,UCS-2所能表C的Unicode只是当今Unicode的一个子集?br />也就是说Q如果某U编码,能根据一定的规则法Q得到Unicode~码Q那么这U编码方式就可以UC为UTF?br />UTF-8和Windows GB2312
UTF-8是一?#8220;聪明”的编码,可能?Q?Q?Q?个字节表C。通过UTF-8的算法,每一个字节表C的信息都很明确Q这是不是某个Unicode~? 码的W一个字节;如果是第一个字节,q是一个几位Unicode~码。这U?#8220;聪明”被称为UTF-8的自我同步,也是UTF-8成ؓ|络传输标准~码的原 因?br />另外QUTF-8也不会出?字节Q所以可以表CZؓchar字符Ԍ所以可以成为系l的~码。Linuxpȝ默认使用UTF-8~码?br />Windows GB2312一般自UCؓGB2312Q其实真正的名字应该是Windows Codepage 936Q这也是一U变长的~码Q?个字节表CZl的ASCII部分Q汉字部分是两个字节的GBKQ国标扩Q展Q,拼音声母Q。Codepage 936也可以表CZؓchar字符Ԍ是中文Windowspȝ的默认编码?br />我们在第1节中看到?br />const char* s = “中文abc”;
在Windows中的~码是Codepage 936Q在Linux中的~码是UTF-8?br />需要注意的是,Codepage 936不像UTFQ跟Unicode没有换算的关p,所以只能通过“代码?#8221;技术查表对应?br />UTF-16和UCS-2
UTF-16?个字节或?个字节表C。在2个字节大的时候,跟UCS-2是一L。UTF-16不像UTF-8Q没有自我同步机Ӟ所以,~码大位 在前q是位在前Q就成了见仁见智的问题。我们在W?节中Q?#8220;?#8221;的UCS-2BEQ因为是两个字节Q所以也是UTF-16BEQ编码是0x4E2DQ? q里的BE是大位在后的意思(也就是小位在前了Q,对应的,如果是UCS-2LEQ编码就成了0x2D4E?br />Windows中的wchar_t是采用UCS-2BE~码。需要指出的是,C++标准中对wchar_t的要求是要能表示所有系l能识别的字W。Windows自称支持UnicodeQ但是其wchar_t却不能表C所有的UnicodeQ由此违背了C++标准?br />UTF-32和UCS-4
UTF-32在目前阶D늭价于UCS-4Q都用定长的4个字节表C。UTF-32同样存在BE和LE的问题。Linux的wchar_t~码是UTF- 32BE。在16位以内的时候,UTF-32BE的后两位Q前两位?×00 0×00Q等价于UTF-16BE也就{h于UCS-2BE
BOM
Z说明一个文仉用的是什么编码,在文件最开始的部分Q可以有BOMQ比?xFE 0xFF表示UTF-16BEQ?xFF 0xFE 0×00 0×00表示UTF-32LE。UTF-8原本是不需要BOM的,因ؓ其自我同步的Ҏ,但是Z明确说明q是UTF-8Q而不是让文本~辑器去猜)Q也 可以加上UTF-8的BOMQ?xEF 0xBB 0xBF
以上内容都讲q得很概略,详细信息h阅维基百U相兛_宏V?/p>
std::locale
通过前面两节的知识,我们知道了在C/C++中,字符Q串Q和宽字W(Ԍ之间的{换不是简单的Q固定的数学关系Q宽H{换依赖于本地化策略集 QlocaleQ。换句话_一个程序在q行之前q不知道pȝ的本地化{略集是什么,E序只有在运行之后才通过locale获得当时的本地化{略集?br />C有自qlocale函数Q我们这里直接介lC++的localecR?br />先讨论locale的构造函敎ͼ
locale() throw();
q个构造函数是获得当前E序的localeQ用法如下:
std::locale app_loc = std::locale();
或者(q是构造对象的两种表示方式Q后同)
std::locale app_loc;
另外一个构造函数是Q?br />explicit locale(const char* name);
q个构造函Cname的名字创建新的locale。重要的locale对象有:
std::locale sys_loc(“”); //获得当前pȝ环境的locale
std::locale C_loc(“C”); 或?nbsp; std::locale C_loc = std::locale::classic(); //获得C定义locale
std::locale old_loc = std::locale::global(new_loc); //new_loc讄为当前全局localeQƈ原来的localeq回lold_loc
除了q些Q其它的name具体名字依赖于C++~译器和操作pȝQ比如Linux下gcc中文pȝ的locale名字?#8221;zh_CN.UTF-8″Q中文Windows可以?#8221;chs”Q更加完整的名字可以用name()函数查看Q?br />mbstowcs()和wcstombs()
q两个Cq行时库函数依赖于全局localeq行转换Q所以,使用前必d讄全局locale?br />std::locale已经包含?lt;iostream>中了Q再加上我们需要用到的C++字符Ԍ所以包?lt;string>?br />我们先看H到宽的转换函数Q?/p>
- const std::wstring s2ws(const std::string& s)
- {
- std::locale old_loc = std::locale::global(std::locale(""));
- const char* src_str = s.c_str();
- const size_t buffer_size = s.size() + 1;
- wchar_t* dst_wstr = new wchar_t[buffer_size];
- wmemset(dst_wstr, 0, buffer_size);
- mbstowcs(dst_wstr, src_str, buffer_size);
- std::wstring result = dst_wstr;
- delete []dst_wstr;
- std::locale::global(old_loc);
- return result;
- }
我们全局locale讄为系llocaleQƈ保存原来的全局locale在old_loc中?br />在制定{换空间缓存大的时候,考虑如下Qchar是用1个或多个对象Q也是1个或者多个字节来表示各种W号Q比如,GB2312?个字节表C数字和 字母Q?个字节表C汉字;UTF-8用一个字节表C数字和字母Q?个字节表C汉字,4个字节表CZ些很用到的W号Q比如音乐中G大调W号{? wchar_t是用1个对象(2字节或?字节Q来表示各种W号。因此,表示同样的字W串Q宽字符串的大小Q也是wchar_t对象的数量)L于? 者等于窄字符串大(char对象数量Q的?1是ؓ了在最后预留一个gؓ0的对象,以便让C风格的char或者wchar_t字符串自动截?#8212;—q当? 是宽串大等于窄串大的时候才会用上的Q大部分时候,字符串早在前面某个{换完毕的位置p0值对象所截断了?br />最后我们将全局locale讄回原来的old_loc?br />H串到宽串的转换函数Q?/p>
- const std::string ws2s(const std::wstring& ws)
- {
- std::locale old_loc = std::locale::global(std::locale(""));
- const wchar_t* src_wstr = ws.c_str();
- size_t buffer_size = ws.size() * 4 + 1;
- char* dst_str = new char[buffer_size];
- memset(dst_str, 0, buffer_size);
- wcstombs(dst_str ,src_wstr, buffer_size);
- std::string result = dst_str;
- delete []dst_str;
- std::locale::global(old_loc);
- return result;
- }
q里考虑转换I间~存大小的策略正好相反,在最极端的情况下Q所有的wchar_t都需?个char来表C,所以最大的可能是4倍加1?br />q两个函数在VC和gcc中都能正常运行(MinGW因ؓ前面说到的原因不支持宽字W的正常使用Q,在VC中会l出不安全的警告Q这是告诉给那些弄不清宽H{换实质的人的警告Q对于了解到目前q些知识的你我来_q就是啰嗦了?/p>
locale和facet
C++的locale框架比C更完备。C++除了一个笼l本地策略集localeQ还可以为locale指定具体的策略facetQ甚臛_以用自己定义? facetL造一个现有的locale产生一个新的locale。如果有一个facetcNewFacet需要添加到某个old_loc中Ş成新 new_locQ需要另外一个构造函敎ͼ通常的做法是Q?br />std::locale new_loc(old_loc, new NewFacet);
标准库里的标准facet都具有自q有的功能Q访问一个locale对象中特定的facet需要用模板函数use_facetQ?br />template <class Facet> const Facet& use_factet(const locale&);
换一U说法,use_facet把一个facetcd例化成了对象Q由此就可以使用q个facet对象的成员函数?br />codecvt
codecvt是一个标准facet。在C++的设计框枉Q这是一个通用的代码{换模?#8212;—也就是说Qƈ不是仅仅为宽H{换制定的?br />templat <class I, class E, class State> class std::codecvt: public locale, public codecvt_base{…};
I表示内部~码QE表示外部~码QState是不同{换方式的标识Q如果定义如下类型:
typedef std::codecvt<wchar_t, char, mbstate_t> CodecvtFacet;
那么CodecvtFacet是一个标准的宽窄转换facetQ其中mbstate_t是标准宽H{换的State?br />内部~码和外部编?br />我们考虑W?节中提到的C++~译器读取源文g时候的情ŞQ当dL”中文abc”的时候,外部~码Q也是源文件的~码Q是GB2312或者UTF-8 的charQ而编译器必须其译为UCS-2BE或者UTF-32BE的wchar_tQ这也就是程序的内部~码。如果不是宽字符Ԍ内外~码都是 charQ也׃需要{换了。类似的Q当C++d文g的时? Q就会可能需要到内外~码转换。事实上Qcodecvt正是被文g缓存basic_filebuf所使用的。理解这一点很重要Q原因会在下一节? 到?br />CodecvtFacet的in()和out()
因ؓ在CodecvtFacet中,内部~码讄为wchar_tQ外部编码设|ؓcharQ{换模式是标准宽窄转换mbstate_tQ所以,cL? in()是从char标准转换到wchar_tQout()是从wchar_t标准转换到char。这成了我们正需要的内外转换函数?br />result in(State& s, const E* from, const E* from_end, const E*& from_next, I* to, I* to_end, I*& to_next) const;
result out(State& s, const I* from, const I* from_end, const I*& from_next, E* to, E* to_end, E*& to_next) const;
其中Qs是非const引用Q保存着转换位移状态信息。这里需要重点强调的是,因ؓ转换的实际工作交l了q行时库Q也是_转换可能不是在程序的主进E? 中完成的Q而{换工作依赖于查询s的|因此Q如果s在{换结束前析构Q就可能抛出q行时异常。所以,最安全的办法是Q将s讄为全局变量Q?br />const?个指针分别是待{换字W串的v点,l点Q和出现错误时候的停点Q的下一个位|)Q另?个指针是转换目标字符串的LQ终点以及出现错误时候的停点Q的下一个位|)?br />代码如下Q?br />头文Ӟ
- //Filename string_wstring_cppcvt.hpp
- #ifndef STRING_WSTRING_CPPCVT_HPP
- #define STRING_WSTRING_CPPCVT_HPP
- #include <iostream>
- #include <string>
- const std::wstring s2ws(const std::string& s);
- const std::string ws2s(const std::wstring& s);
- #endif
实现Q?/p>
- #include "string_wstring_cppcvt.hpp"
- mbstate_t in_cvt_state;
- mbstate_t out_cvt_state;
- const std::wstring s2ws(const std::string& s)
- {
- std::locale sys_loc("");
- const char* src_str = s.c_str();
- const size_t BUFFER_SIZE = s.size() + 1;
- wchar_t* intern_buffer = new wchar_t[BUFFER_SIZE];
- wmemset(intern_buffer, 0, BUFFER_SIZE);
- const char* extern_from = src_str;
- const char* extern_from_end = extern_from + s.size();
- const char* extern_from_next = 0;
- wchar_t* intern_to = intern_buffer;
- wchar_t* intern_to_end = intern_to + BUFFER_SIZE;
- wchar_t* intern_to_next = 0;
- typedef std::codecvt<wchar_t, char, mbstate_t> CodecvtFacet;
- CodecvtFacet::result cvt_rst =
- std::use_facet<CodecvtFacet>(sys_loc).in(
- in_cvt_state,
- extern_from, extern_from_end, extern_from_next,
- intern_to, intern_to_end, intern_to_next);
- if (cvt_rst != CodecvtFacet::ok) {
- switch(cvt_rst) {
- case CodecvtFacet::partial:
- std::cerr << "partial";
- break;
- case CodecvtFacet::error:
- std::cerr << "error";
- break;
- case CodecvtFacet::noconv:
- std::cerr << "noconv";
- break;
- default:
- std::cerr << "unknown";
- }
- std::cerr << ", please check in_cvt_state."
- << std::endl;
- }
- std::wstring result = intern_buffer;
- delete []intern_buffer;
- return result;
- }
- const std::string ws2s(const std::wstring& ws)
- {
- std::locale sys_loc("");
- const wchar_t* src_wstr = ws.c_str();
- const size_t MAX_UNICODE_BYTES = 4;
- const size_t BUFFER_SIZE =
- ws.size() * MAX_UNICODE_BYTES + 1;
- char* extern_buffer = new char[BUFFER_SIZE];
- memset(extern_buffer, 0, BUFFER_SIZE);
- const wchar_t* intern_from = src_wstr;
- const wchar_t* intern_from_end = intern_from + ws.size();
- const wchar_t* intern_from_next = 0;
- char* extern_to = extern_buffer;
- char* extern_to_end = extern_to + BUFFER_SIZE;
- char* extern_to_next = 0;
- typedef std::codecvt<wchar_t, char, mbstate_t> CodecvtFacet;
- CodecvtFacet::result cvt_rst =
- std::use_facet<CodecvtFacet>(sys_loc).out(
- out_cvt_state,
- intern_from, intern_from_end, intern_from_next,
- extern_to, extern_to_end, extern_to_next);
- if (cvt_rst != CodecvtFacet::ok) {
- switch(cvt_rst) {
- case CodecvtFacet::partial:
- std::cerr << "partial";
- break;
- case CodecvtFacet::error:
- std::cerr << "error";
- break;
- case CodecvtFacet::noconv:
- std::cerr << "noconv";
- break;
- default:
- std::cerr << "unknown";
- }
- std::cerr << ", please check out_cvt_state."
- << std::endl;
- }
- std::string result = extern_buffer;
- delete []extern_buffer;
- return result;
- }
最后补充说明一下std::use_facet<CodecvtFacet>(sys_loc).in()? std::use_facet<CodecvtFacet>(sys_loc).out()。sys_loc是系l的localeQ这? locale中就包含着特定的codecvt facetQ我们已ltypedefZCodecvtFacet。用use_facet对CodecvtFacetq行了实例化Q所以可以用这? facet的方法in()和out()?/p>
C++的流和本地化{略?br />BS在设计C++的时候希望其具备化,q且是可扩展的智能化Q也是_C++的流可以“L”一些内宏V比如:
std::cout << 123 << “ok” << std::endl;
q句代码中,std::cout是能判断?23是int?#8221;ok”是const char[3]。利用流的智能,甚至可以做一些基cd的{换,比如从int到stringQstring到intQ?/p>
- std::string str("123");
- std::stringstream sstr(str);
- int i;
- sstr >> i;
- int i = 123;
- std::stringstream sstr;
- sstr << i;
- std::string str = sstr.str();
管如此QC++q不满QC++甚至希望能“明白”旉Q货币的表示法。而时间和货币的表C方法在世界范围内是不同的,所以,每一个流都有自己 的locale在媄响其行ؓQC++中叫做激z(imbueQ也有翻译成染Q。而我们知道,每一个locale都有多个facetQ这些facetq L被use_facet使用的。决定用哪些facet的,是流的缓存basic_streambuf及其zcbasic_stringbuf? basic_filebuf。我们要用到的facet是codecvtQ这个facet只被basic_filebuf使用——q就是ؓ什么只能用 fstream来实现宽H{换,而无法用sstream来实现的原因?br />头文Ӟ
- //filename string_wstring_fstream.hpp
- #ifndef STRING_WSTRING_FSTREAM_HPP
- #define STRING_WSTRING_FSTREAM_HPP
- #include <string>
- const std::wstring s2ws(const std::string& s);
- const std::string ws2s(const std::wstring& s);
- #endif
实现Q?/p>
- #include <string>
- #include <fstream>
- #include "string_wstring_fstream.hpp"
- const std::wstring s2ws(const std::string& s)
- {
- std::locale sys_loc("");
- std::ofstream ofs("cvt_buf");
- ofs << s;
- ofs.close();
- std::wifstream wifs("cvt_buf");
- wifs.imbue(sys_loc);
- std::wstring wstr;
- wifs >> wstr;
- wifs.close();
- return wstr;
- }
- const std::string ws2s(const std::wstring& s)
- {
- std::locale sys_loc("");
- std::wofstream wofs("cvt_buf");
- wofs.imbue(sys_loc);
- wofs << s;
- wofs.close();
- std::ifstream ifs("cvt_buf");
- std::string str;
- ifs >> str;
- ifs.close();
- return str;
- }
在窄到宽的{化中Q我们先使用默认的本地化{略集(localeQ将s通过H文件流ofs传入文gQ这是char到char的传递,没有M转换Q? 然后我们打开宽文件流wifsQƈ用系l的本地化策略集QlocaleQ去Ȁz(imbueQ之Q流在读回宽串wstr的时候,是char? wchar_t的{换,q且因ؓȀzMsys_locQ所以实现标准窄到宽的{换?br />在宽到窄的{化中Q我们先打开的是宽文件流wofsQƈ且用pȝ的本地化{略集sys_locȀz(imbueQ之Q这时候,因ؓ要写的文? cvt_buf是一个外部编码,所以执行了从wchar_t到char的标准{换。读回来的文件流从char到charQ不做Q何{换?/p>
编码的伤
我们现在知道QC/C++的宽H{换是依赖pȝ的locale的,q且在运行时完成。考虑q样一U情况,我们在简体中文Windows下编译如下语句:
const char* s = “中文abc”;
Ҏ我们之前的讨论,~译器将按照Windows Codepage936QGB2312Q对q个字符串进行编码。如果我们在E序中运行宽H{换函敎ͼs转换为宽字符串wsQ如果这个程序运行在体中? 环境下是没问题的Q将执行从GB2312到UCS-2BE的{换;但是Q如果在其他语言环境下,比如是繁体中文BIG5Q程序将Ҏpȝ的locale? 行从BIG5到UCS-2BE的{换,q显然就出现了错误?br />补救
有没有补救这个问题的办法呢?一个解x案就是执行不依赖locale的宽H{换。实际上Q这已l不是宽H{换之间的问题了,而是~码之间转换的问? 了。我们可以用GNU的libiconv实现L~码间的转换Q对于以上的具体情况Q指明是从GB2312到UCS-2BE׃会出错。(请参考本人前?的章节:win32下的libiconvQ,但这昄是一个笨拙的{略Q我们在体中文Windows下必M用GB2312到UCS-2BE版本的宽H{换函敎ͼCBIG5环境下,必重新写从BIG5到UCS-2BE的宽H{换函数?br />Windows的策?br />Windows的策略是淘汰了窄字符Ԍq脆只用宽字W串。所有的编码全部加上特定宏Q比如TEXT()Q如果程序是所谓Unicode~译Q在~译时就译为UCS2-BE——Windows自称为Unicode~程Q其本质是用了UCS-2BE?6位宽字符丌Ӏ?br />Linux的策?br />Linux下根本就不存在这个问题!因ؓ各种语言的Linux都用UTF-8的编码,所以,无论pȝlocale如何变化Q窄到宽转换的规则一直是UTF-8到UTF32-BE ?br />跨^台策?br />因ؓ?6位的范围内,UTF32-BE的前16位ؓ0Q后16位与UCS2-BE是一LQ所以,即wchar_t的sizeof()不一P在一般情况下Q跨q_使用宽字W(Ԍ也应该是兼容的。但是依然存在潜在的问题Q就是那?字节的UTF32~码?br />gettext{略
以上都是ASCII及以外的~码编码在E序中的办法。GNU的gettext提供了另外一U选择Q在E序中只编码ASCIIQ多语言支持由gettext函数库在q行时加载。(对gettext的介l请参考本人前面的章节Q?a href="http://www.shnenglu.com/deane/archive/2011/lf426/archive/2008/03/30/45723.html" target="\"_blank\"">Win32下的GetTextQ? gettext的多语言译文g不在E序中,而是单独的提出来攑֜特定的位|。gettext明确的知道这些翻译文件的~码Q所以可以准的告诉l系l翻 译的正确信息Q而系l将q些信息以当前的pȝlocale~码成窄字符串反馈给E序。例如,在简体中文Windows中,gettext的po文g也可? 以UTF-8储存Qgettextpo文g译成mo文gQ确保mo文g在Q何系l和语言环境下都能够正确译。在q行是传lwin32E序的窄串符? 当前localeQ是GB2312。gettext让国际化的翻译更加的方便Q缺Ҏ目前我没扑ֈ支持宽字W串的版本(据说是有ugettext()支持 宽字W串Q,所以要使用gettext只能使用H字W串。但是gettext可以转换到宽字符Ԍ而且不会出现宽窄转换的问题,因ؓgettext是运? 时根据locale译的。例如:
const char* s = gettext(“Chinese a b c”);
其中”Chinese a b c”在po中的译?#8221;中文abc”
使用依赖locale的运行时宽窄转换函数Q?br />const std::wstring wstr = s2ws(s);
q行时调用该po文g对应的mo文gQ在体中文环境下׃GB2312传给E序Q在J体中文中就以BIG5传给E序Q这样s2ws()总能够正常换编码?br />更多
在本文的最后,我想回到C++的stream问题上。用fstream转换如此的简单,sstream却不支持。改造一个支持codecvt? string stream需要改造basic_stringbuf。basic_stringbuf和basic_filebuf都派生自 basic_streambufQ所不同的是basic_filebuf在构造和open()的时候调用了codecvtQ只需要在 basic_stringbuf中添加这个功能就可以了。说hҎQ实际上是需要重新改造一个STL模板Q尽这些模板源代码都是在标准库头文件中现成 的,但是我还是水qx限,没有LI了。另外一个思\是构Z个基于内存映的虚拟文gQ这个框架在boost的iostreams库中Q有兴趣的朋友可 以深入的研究?br />Q完Q?/p>Related Posts: