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

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()。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值