关于本地化的学习心得 阅读书籍<C++输入输出流与本地化>
本地化都是通过locale和facet这两个类来实现的
所有基本的国际化方法都在标准facet中提供。facet提供文化差异的服务和信息。
名次解释:国际化:尽量使用通用的做法以及提供多种方法以适应不同语言文化的地区使用。
本地化:利用国际化中提供的方法,改变软件的行为以适应本地区语言文化的要求。
第一:可以看到,locale已经把facet组合在里面了,且所有locale对象都有一个公共的全局对象global,
目前电脑上这个全局global基本上就是"C"; 如果用户没有指定一个locale,那么系统默认使用这个。
第二:所有操作函数都具有const的声明,这说明locale对象一旦创建,其内容即不可更改。
第三:locate对象的,可以由下面的7个构造函数之一创建,
每个构造函数创建有不同意义的locale 对象,以下就不一一说明了。
locale详细定义在C++标准里面的定义(C++标准白皮书2003第二版)可自行查阅,或在locale头文件中察看。
namespace std {
class locale {
public:
// types:
class facet;
class id;
typedef int category;
static const category // values assigned here are for exposition only
none = 0,
collate = 0x010, ctype = 0x020,
monetary = 0x040, numeric = 0x080,
time = 0x100, messages = 0x200,
all = collate | ctype | monetary | numeric | time | messages;
// construct/copy/destroy:
locale() throw(); //11
locale(const locale& other) throw(); //22
explicit locale(const char* std_name); //33
locale(const locale& other, const char* std_name, category); //44
template <class Facet> locale(const locale& other, Facet* f); //55
locale(const locale& other, const locale& one, category); //66
~locale() throw(); // non-virtual
const locale& operator=(const locale& other) throw(); //77
template <class Facet> locale combine(const locale& other) const;
//这个用于结合多个locale对象形成一个新的未命名locale对象
//比如,可以结合德国+法国的某个特性。形成一个德法混合本地对象。
// locale operations:
basic_string<char> name() const;
bool operator==(const locale& other) const;
bool operator!=(const locale& other) const;
template <class charT, class Traits, class Allocator>
bool operator()(const basic_string<charT,Traits,Allocator>& s1,
const basic_string<charT,Traits,Allocator>& s2) const;
// global locale objects:
static locale global(const locale&);
static const locale& classic();
};
}
facet 有很多种:
ctype(字符分类),
codecvt(编码转换),
collate(校对),
numputct(数字标点信息),
num_get(数字解析),
num_put(数字格式化),
moneyputct(货币标点信息),
money_get(货币解析),
money_put(货币格式化),
time_get(时间解析),
time_put(时间格式化)
由此我们可以实现自己的facet就可以自由定制编码的输入输出,从而实现本地化的目标。
我们现在想建立一个Unicode的文本文件,在C++中需要自己实现一个facet,
否则就只有使用C函数_wfopen...等等。
如何实现:既实现 codecvt<wchar_t,char, mbstate_t> 转换的Unicode 的特化的facet版本。
关于facet
facet
ctype(字符分类有:)alpha,digit,crtl,lower,print,punc(标点),space,upper,xdigit(16进制的0-F),alnum,graph等。
成员函数is():判定是否所属分类;为方便使用,std重新封装,变成isspace,isalpha..等形式。
std::tolower,std::toupper也是调用的ctype的成员函数tolower,toupper实现的,可找到老家了。
ctype另两个有用的成员函数是:narrow,widen:用于宽字符和窄字符之间的转换。
ctype还有一个串整理collate,用于字符的排序比较。这个我不太关心,我觉得让他按字符集的排列排序就可以了,呵呵。
codecvt
下面重点说一下codecvt
codecvt(编码转换),呵呵,我们要用的就是它,它就是在多字节编码和宽字符编码之间进行转换。
我们的目标就是继承他,并且实现我们自己的编码转换方案,并将它替换到一个locale对象上的facet上,
通过使用替换后的locale对象 ,达到实现特定编码集之间进行转换的目的。
这样我们有必要了解其每一个成员函数都是具体做什么工作的。
其定义是:
template<internT,extranT,stateT> class codecvt
模板参数:internT //与内部编码集相关的字符集,所谓内部编码, 一般都是内存里整型变量、数组等。
模板参数:externT //与外部编码集相关的字符集,所谓外部编码,指的是外部设备。文件、管道,socket
模板参数:stateT //用于控制内外编码转换时状态的。
codecvt 提供的成员函数分为两种:一种负责提供编码转换信息的,一种就是提供编码转换功能的。
负责提供编码转换信息有5个:
always_noconv() //:指出是否需要编码转换。
encoding() //: 指出是否转换与特定国家相关,外字符集/内字符集的比例是否固定,这个比例是多少?
length() //: 确定,对于给出一个外部字符集的字符,那么其内部字符的长度是多少。
MAX_length() //: 返回一个内部字符需要外部字符的最大长度。
unshift() //: 当外部字符与 与内部字符相关时,提供反向操作;
负责提供编码转换有2个:
in() //:执行从外部编码向内部编码转换
out() //:与in相反。
以前我们已经把对字节编码转化成了Unicode编码,输出屏幕也正确,可是一旦写入文件就会不正确
由此可以猜测到,文件流在进行写入操作时,一定是使用了系统默认提供的facet对编码进行了宽字符到多字节的编码转化。
我们现在要做的,就是使用自己定义的facet,要求文件流操作不要对编码进行转化即可。
因为我们事先已经通过mbstowcs或者MultiByteToWideChar转换好了。由此我写出了如下的facet
//这个
class UniFacet:public codecvt <wchar_t ,char , mbstate_t >
{
protected:
virtual result do_in( mbstate_t&,
const wchar_t* , const wchar_t* , const wchar_t& ,
wchar_t, wchar_t* , wchar_t&) const {return noconv ;}
virtual result do_out( mbstate_t& ,
const wchar_t* , const wchar_t* , const wchar_t*,
wchar_t*, wchar_t*, wchar_t& ) const {return noconv ;}
virtual result do_unshift( mbstate_t&,
wchar_t* , wchar_t* , wchar_t*) const {return noconv ;}
virtual int do_length( mbstate_t& , const wchar_t* ,
const wchar_t* , size_t ) const _THROW0() {return 1;}
virtual bool do_always_noconv() const _THROW0() {return true;}
virtual int do_max_length() const _THROW0() {return 1;}
virtual int do_encoding() const _THROW0() {return 1;}
};
仔细看一下,这个facet作了什么转化?nothing!,对!这正是我们需要的,告诉调用者,什么都别作,原样照搬即可。
我们将通过locale 间接使用这个facet
由此我写出了如下的测试代码:验证了这个推测,不过我现在也是初窥门径,还有很多没搞清楚,总算有所得。
完整程序如下://在window下。vs2005下运行通过。
#include <locale>
#include <fstream>
using namespace std;
class UniFacet:public codecvt <wchar_t ,char , mbstate_t >
{
protected:
virtual result do_in( mbstate_t&,
const wchar_t* , const wchar_t* , const wchar_t& ,
wchar_t, wchar_t* , wchar_t&) const {return noconv ;}
virtual result do_out( mbstate_t& ,
const wchar_t* , const wchar_t* , const wchar_t*,
wchar_t*, wchar_t*, wchar_t& ) const {return noconv ;}
virtual result do_unshift( mbstate_t&,
wchar_t* , wchar_t* , wchar_t*) const {return noconv ;}
virtual int do_length( mbstate_t& , const wchar_t* ,
const wchar_t* , size_t ) const _THROW0() {return 1;}
virtual bool do_always_noconv() const _THROW0() {return true;}
virtual int do_max_length() const _THROW0() {return 1;}
virtual int do_encoding() const _THROW0() {return 1;}
};
int main()
{
//建一个Unicode的文本文件。
wchar_t wch[100]=L"a中文Chinese与英文English混排ss/",,可用MultiByteToWideChar转换,也可用mbstowcs转换";
wstring wstr=L"真是折腾人阿!!!";
wofstream UniFile;
UniFile.open("C://UniFile.txt",ios::out|ios::binary); //
locale locUni(locale::classic(),new UniFacet);
//这句解释一下,local::classic() 是loccale的一个静态成员函数,他返回一个当前使用的
//locale对象的一个引用,我们现在通过locale的一个构造函数,重新构造一个新的locale对象
//locUni,并把其中codecvt这个facet换成我们自己定义的UniFacet.
UniFile<<UniFile.narrow(0xFF); //设置文件标志;这两是窄字符,一般Unicode文本文件
UniFile<<UniFile.narrow(0xFE); //都以这两个字符作为其起始标志位。
UniFile.imbue(locUni); //改变locale;
UniFile<<wch<<UniFile.widen('/r')<<UniFile.widen('/n'); //回车换行都要宽字符化
UniFile<<wstr<<UniFile.widen('/r')<<UniFile.widen('/n');
UniFile<<"123456"<<UniFile.widen('/r')<<UniFile.widen('/n');
UniFile<<L"ABCDE"<<UniFile.widen('/r')<<UniFile.widen('/n');
UniFile<<34545678<<UniFile.widen('/r')<<UniFile.widen('/n');
UniFile<<L"这样就可以了"<<UniFile.widen('/r')<<UniFile.widen('/n');
UniFile.close();
system("pause");
return 0;
}
//UniFile<<"123456"<<UniFile.widen('/r')<<UniFile.widen('/n');
//这句"123456"为什么也被正确转换为Unicode编码了,猜测wfostranm一定有<<的重载版本在帮忙。
//且只有ASCII基本字符集里才能被正确转换。
//这个我就没有再细细追究了。
<C++输入输出流与本地化>给了理论指导,
下面我找到的一片文章,给了实践指导。这里地址给出来,可以参考下。
http://www.codeproject.com/vcpp/stl/upgradingstlappstounicode.asp?df=100&forumid=16224&exp=0&select=788194