您的位置:首页 > 编程语言 > C语言/C++

彻底解密C++宽字符:4、利用codecvt和use_facet转换

2011-03-11 11:09 323 查看
原文链接: http://www.cppblog.com/lf426/archive/2010/06/26/118772.html



locale和facet

C++的locale框架比C更完备。C++除了一个笼统本地策略集locale,还可以为locale指定具体的策略facet,甚至可以用自己定义的facet去改造一个现有的locale产生一个新的locale。如果有一个facet类NewFacet需要添加到某个old_loc中形成新new_loc,需要另外一个构造函数,通常的做法是:
std::locale new_loc(old_loc, new NewFacet);
标准库里的标准facet都具有自己特有的功能,访问一个locale对象中特定的facet需要使用模板函数use_facet:
template <class Facet> const Facet& use_factet(const locale&);
换一种说法,use_facet把一个facet类实例化成了对象,由此就可以使用这个facet对象的成员函数。

codecvt

codecvt就是一个标准facet。在C++的设计框架里,这是一个通用的代码转换模板——也就是说,并不是仅仅为宽窄转换制定的。
templat <class I, class E, class State> class std::codecvt: public locale, public codecvt_base{...};
I表示内部编码,E表示外部编码,State是不同转换方式的标识,如果定义如下类型:
typedef std::codecvt<wchar_t, char, mbstate_t> CodecvtFacet;
那么CodecvtFacet就是一个标准的宽窄转换facet,其中mbstate_t是标准宽窄转换的State。

内部编码和外部编码

我们考虑第1节中提到的C++编译器读取源文件时候的情形,当读到L"中文abc"的时候,外部编码,也就是源文件的编码,是GB2312或者UTF-8的char,而编译器必须将其翻译为UCS-2BE或者UTF-32BE的wchar_t,这也就是程序的内部编码。如果不是宽字符串,内外编码都是char,也就不需要转换了。类似的,当C++读写文件的时候 ,就会可能需要到内外编码转换。事实上,codecvt就正是被文件流缓存basic_filebuf所使用的。理解这一点很重要,原因会在下一小节看到。

CodecvtFacet的in()和out()
因为在CodecvtFacet中,内部编码设置为wchar_t,外部编码设置为char,转换模式是标准宽窄转换mbstate_t,所以,类方法in()就是从char标准转换到wchar_t,out()就是从wchar_t标准转换到char。这就成了我们正需要的内外转换函数。
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;
其中,s是非const引用,保存着转换位移状态信息。这里需要重点强调的是,因为转换的实际工作交给了运行时库,也就是说,转换可能不是在程序的主进程中完成的,而转换工作依赖于查询s的值,因此,如果s在转换结束前析构,就可能抛出运行时异常。所以,最安全的办法是,将s设置为全局变量!
const的3个指针分别是待转换字符串的起点,终点,和出现错误时候的停点(的下一个位置);另外3个指针是转换目标字符串的起点,终点以及出现错误时候的停点(的下一个位置)。

代码如下:
头文件

//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
实现:

#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是系统的locale,这个locale中就包含着特定的codecvt facet,我们已经typedef为了CodecvtFacet。用use_facet对CodecvtFacet进行了实例化,所以可以使用这个facet的方法in()和out()。





********************************************************************************************



同时, 可以通过模板,将字符串同其他类型相互转化:

http://hi.baidu.com/gookings/blog/item/9ea4b16e5e95ecca81cb4ab7.html



转换中使用模板

也可以轻松地定义函数模板来将一个任意的类型转换到特定的目标类型。例如,需要将各种数字值,如int、long、double等等转换成字符串,要使用以一个string类型和一个任意值t为参数的to_string()函数。to_string()函数将t转换为字符串并写入result中。使用str()成员函数来获取流内部缓冲的一份拷贝:

示例3:

template<class T>

void to_string(string & result,const T& t)

{

ostringstream oss;//创建一个流

oss<<t;//把值传递如流中

result=oss.str();//获取转换后的字符转并将其写入result
}

这样,你就和衣轻松地将多种数值转换成字符串了:

to_string(s1,10.5);//double到string

to_string(s2,123);//int到string

to_string(s3,true);//bool到string

可以更进一步定义一个通用的转换模板,用于任意类型之间的转换。函数模板convert()含有两个模板参数out_type和in_value,功能是将in_value值转换成out_type类型:

template<class out_type,class in_value>

out_type convert(const in_value & t)

{

stringstream stream;

stream<<t;//向流中传值

out_type result;//这里存储转换结果

stream>>result;//向result中写入值

return result;

}

这样使用convert():

double d;

string salary;

string s=”12.56”;

d=convert<double>(s);//d等于12.56

salary=convert<string>(9000.0);//salary等于”9000”

结论

在过去留下来的程序代码和纯粹的C程序中,传统的<stdio.h>形式的转换伴随了我们很长的一段时间。但是,如文中所述,基于stringstream的转换拥有类型安全和不会溢出这样抢眼的特性,使我们有充足得理由抛弃<stdio.h>而使用<sstream>。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: