<本文PDF文檔下載>locale和facet
C++的locale框架比C更完備。C++除了一個籠統(tǒng)本地策略集locale,還可以為locale指定具體的策略facet,甚至可以用自己定義的facet去改造一個現(xiàn)有的locale產(chǎn)生一個新的locale。如果有一個facet類NewFacet需要添加到某個old_loc中形成新new_loc,需要另外一個構(gòu)造函數(shù),通常的做法是:
std::locale new_loc(old_loc, new NewFacet);
標(biāo)準(zhǔn)庫里的標(biāo)準(zhǔn)facet都具有自己特有的功能,訪問一個locale對象中特定的facet需要使用模板函數(shù)use_facet:
template <class Facet> const Facet& use_factet(const locale&);
換一種說法,use_facet把一個facet類實(shí)例化成了對象,由此就可以使用這個facet對象的成員函數(shù)。
codecvt
codecvt就是一個標(biāo)準(zhǔn)facet。在C++的設(shè)計(jì)框架里,這是一個通用的代碼轉(zhuǎn)換模板——也就是說,并不是僅僅為寬窄轉(zhuǎn)換制定的。
templat <class I, class E, class State> class std::codecvt: public locale, public codecvt_base{...};
I表示內(nèi)部編碼,E表示外部編碼,State是不同轉(zhuǎn)換方式的標(biāo)識,如果定義如下類型:
typedef std::codecvt<wchar_t, char, mbstate_t> CodecvtFacet;
那么CodecvtFacet就是一個標(biāo)準(zhǔn)的寬窄轉(zhuǎn)換facet,其中mbstate_t是標(biāo)準(zhǔn)寬窄轉(zhuǎn)換的State。
內(nèi)部編碼和外部編碼
我們考慮第1節(jié)中提到的C++編譯器讀取源文件時候的情形,當(dāng)讀到L"中文abc"的時候,外部編碼,也就是源文件的編碼,是GB2312或者UTF-8的char,而編譯器必須將其翻譯為UCS-2BE或者UTF-32BE的wchar_t,這也就是程序的內(nèi)部編碼。如果不是寬字符串,內(nèi)外編碼都是char,也就不需要轉(zhuǎn)換了。類似的,當(dāng)C++讀寫文件的時候 ,就會可能需要到內(nèi)外編碼轉(zhuǎn)換。事實(shí)上,codecvt就正是被文件流緩存basic_filebuf所使用的。理解這一點(diǎn)很重要,原因會在下一小節(jié)看到。
CodecvtFacet的in()和out()
因?yàn)樵贑odecvtFacet中,內(nèi)部編碼設(shè)置為wchar_t,外部編碼設(shè)置為char,轉(zhuǎn)換模式是標(biāo)準(zhǔn)寬窄轉(zhuǎn)換mbstate_t,所以,類方法in()就是從char標(biāo)準(zhǔn)轉(zhuǎn)換到wchar_t,out()就是從wchar_t標(biāo)準(zhǔn)轉(zhuǎn)換到char。這就成了我們正需要的內(nèi)外轉(zhuǎn)換函數(shù)。
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引用,保存著轉(zhuǎn)換位移狀態(tài)信息。這里需要重點(diǎn)強(qiáng)調(diào)的是,因?yàn)檗D(zhuǎn)換的實(shí)際工作交給了運(yùn)行時庫,也就是說,轉(zhuǎn)換可能不是在程序的主進(jìn)程中完成的,而轉(zhuǎn)換工作依賴于查詢s的值,因此,如果s在轉(zhuǎn)換結(jié)束前析構(gòu),就可能拋出運(yùn)行時異常。所以,最安全的辦法是,將s設(shè)置為全局變量!
const的3個指針分別是待轉(zhuǎn)換字符串的起點(diǎn),終點(diǎn),和出現(xiàn)錯誤時候的停點(diǎn)(的下一個位置);另外3個指針是轉(zhuǎn)換目標(biāo)字符串的起點(diǎn),終點(diǎn)以及出現(xiàn)錯誤時候的停點(diǎn)(的下一個位置)。
代碼如下:
頭文件
//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
實(shí)現(xiàn):
#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;
}
最后補(bǔ)充說明一下std::use_facet<CodecvtFacet>(sys_loc).in()和std::use_facet<CodecvtFacet>(sys_loc).out()。sys_loc是系統(tǒng)的locale,這個locale中就包含著特定的codecvt facet,我們已經(jīng)typedef為了CodecvtFacet。用use_facet對CodecvtFacet進(jìn)行了實(shí)例化,所以可以使用這個facet的方法in()和out()。