標籤:

在C++11中,如何將一種編碼的string轉換為另一種編碼的string?

比如將一個GBK編碼的string轉換為UTF-8的string,用boost庫可以輕而易舉地解決:

#include &
#include &
inline std::string tr(const std::string str) {
return boost::locale::conv::between(str, "UTF-8", "GBK");
}

請問在C++11中,如何使用標準庫設施(如concvt等)來解決這個問題?


C++11的std::wstring_convert配合std::codecvt模板類完全可以解決這個問題,不會出現@vczh 所說修改了全局locale會導致污染其他庫的問題。

這兩個模板類的功能是:

std::wstring_convert:轉碼器,接收一個類似codecvt描述編碼轉換特性的模板參數,用於將本地化的寬字元wstring和指定編碼的位元組化string進行互轉。

std::codecvt:編碼轉換特性類,用在wstring_convert的模板參數中來指定使用哪種編碼。

所以編碼A和B互轉的實現方式就是:藉助本地化寬字元串,先將以A編碼的string轉為本地化的wstring,再將本地化的wstring轉為B編碼後的string。

codecvt一般使用下面兩個特化子類:

std::codecvt_utf8&:用於UTF8和本地化wchar_t的互轉

std::codecvt_byname&:用於其他編碼(例如GBK)和本地化wchar_t的互轉,類的構造函數需要傳入編碼的locale name,由於編碼的locale name是操作系統決定的(例如GBK在linux下的locale名可能是"zh_CN.GBK",而windows下是".936"),因此做跨平台的話仍然要給不同的系統做適配。

這裡給一個windows下,GBK string轉UTF8 string的例子:

首先將GBK string轉wstring

const char* GBK_LOCALE_NAME = ".936"; //GBK在windows下的locale name
string gbk_str {"xCCxCC"}; //0xCCCC,"燙"的GBK碼

//構造GBK與wstring間的轉碼器(wstring_convert在析構時會負責銷毀codecvt_byname,所以不用自己delete)
wstring_convert&&> cv1(new codecvt_byname&(GBK_LOCALE_NAME));
wstring tmp_wstr = cv1.from_bytes(gbk_str);

再將wstring轉為UTF8 string

wstring_convert&&> cv2;
string utf8_str = cv.to_bytes(tmp_wstr);

轉碼就完成了。utf8_str里的內容應該是"xE7x83xAB"(燙的UTF8)。


在Windows下面你就應該用API,OSX也有自己的API,Linux還有自己locale的庫。最好的方法就是自己對這個函數寫三遍,在不同的操作系統下鏈接不同的實現。

C和C++ runtime帶的函數,locale的設置是全局的,一不小心就會跟別的庫衝突。不要這麼做,避免各種爛事。


題主可以參考 std::codecvt 和 std::wstring_convert

wstring_convert 類

&


GCC 5.0 libc++ 還有 Visual C++ 2012 支持編碼轉換 ,頭文件是 codecvt,

bool WideStringToString(const std::wstring src,std::string str)
{
std::locale sys_locale("");

const wchar_t* data_from = src.c_str();
const wchar_t* data_from_end = src.c_str() + src.size();
const wchar_t* data_from_next = 0;

int wchar_size = 4;
char* data_to = new char[(src.size() + 1) * wchar_size];
char* data_to_end = data_to + (src.size() + 1) * wchar_size;
char* data_to_next = 0;

memset( data_to, 0, (src.size() + 1) * wchar_size );

typedef std::codecvt& convert_facet;
mbstate_t out_state = {0};
auto result = std::use_facet&(sys_locale).out(
out_state, data_from, data_from_end, data_from_next,
data_to, data_to_end, data_to_next );
if( result == convert_facet::ok)
{
str = data_to;
delete[] data_to;
return true;
}
delete[] data_to;
return false;
}

bool StringToWideString( const std::string src,std::wstring wstr)
{
std::locale sys_locale("");
const char* data_from = src.c_str();
const char* data_from_end = src.c_str() + src.size();
const char* data_from_next = 0;

wchar_t* data_to = new wchar_t[src.size() + 1];
wchar_t* data_to_end = data_to + src.size() + 1;
wchar_t* data_to_next = 0;

wmemset( data_to, 0, src.size() + 1 );

typedef std::codecvt& convert_facet;
mbstate_t in_state = {0};
auto result = std::use_facet&(sys_locale).in(
in_state, data_from, data_from_end, data_from_next,
data_to, data_to_end, data_to_next );
if( result == convert_facet::ok )
{
wstr = data_to;
delete[] data_to;
return true;
}
delete[] data_to;
return false;
}

bool WCharStringToUTF8String(const std::wstring wstr,std::string u8str)
{
std::wstring_convert&&> conv;
u8str= conv.to_bytes(wstr);
return true;
}

bool UTF8StringToWCharString(const std::string u8str,std::wstring wstr)
{
std::wstring_convert& &> conv;
wstr=conv.from_bytes( u8str );
return true;
}


// 貼段公司的代碼吧,wchar_t在不同系統尺寸不一樣?所以包裝了個vxUWChar
void utf162utf8(const vxUWChar* str, char* dst,int lens)
{
#ifdef _WIN32
WideCharToMultiByte(CP_UTF8,0,(LPCWSTR)str,(int)vxUWcslen(str),dst,lens,NULL,NULL);
#elif defined(__APPLE__)
CFStringRef strref = CFStringCreateWithBytes(kCFAllocatorDefault,(const UInt8*)str,vxUWcslen(str)*2,kCFStringEncodingUTF16,false);
if(strref)
{
CFStringGetCString(strref,(char*)dst,lens,kCFStringEncodingUTF8);
CFRelease(strref);
}
#else
iconv_t cd = iconv_open("UTF-8", "UTF-16LE");
size_t insize = vxUWcslen(str)*2;
size_t outsize = lens;
int ret = iconv(cd,(char**)str,insize,(char**)dst,outsize);
iconv_close(cd);
#endif
}


推薦閱讀:

如何將一個函數編譯成二進位文件?
微軟對 C++ 的影響力有多大?
為什麼要有堆區和棧區呢?
如何將一個有序的數組隨機排序?
從事C#開發兩年,25歲轉Cocos2d-x開發,會不會太晚?

TAG:C | CC | C11 |