C++中字符編碼的轉(zhuǎn)換(Unicode、UTF-8、ANSI)
作者:FlushHip
在處理東方語(yǔ)言(中日韓)時(shí),經(jīng)常會(huì)遇到各種編碼問(wèn)題,而且被這類(lèi)問(wèn)題搞的暈頭轉(zhuǎn)向。到網(wǎng)上查資料,看的也是一頭霧水,最后往往是誤打誤撞的把問(wèn)題解決了,自己仍然稀里糊涂。
這篇文章介紹了如何在最常見(jiàn)的編碼方式(Unicode, UTF-8, ANSI)之間進(jìn)行轉(zhuǎn)換,結(jié)合代碼實(shí)例,清晰明了,方便讀者理解,例子也可以直接拿來(lái)使用。本文推薦給經(jīng)常對(duì)文字字符串進(jìn)行處理的程序員閱讀,使其掌握字符轉(zhuǎn)換的一些基本方法。
正文
------------?strongerHuang?------------
C++的項(xiàng)目,字符編碼是一個(gè)大坑,不同平臺(tái)之間的編碼往往不一樣,如果不同編碼格式用一套字符讀取格式讀取就會(huì)出現(xiàn)亂碼。因此,一般都是轉(zhuǎn)化成UTF-8這種平臺(tái)通用,且支持性很好的編碼格式。
Unicode、UTF-8的概念不做過(guò)多解釋?zhuān)@里說(shuō)一下ANSI,我第一次看到這個(gè)名詞,我看成了ASCII。被Mentor狠批一頓。
ANSI是一種字符代碼,為使計(jì)算機(jī)支持更多語(yǔ)言,通常使用 0x00 ~ 0x7F范圍的1 個(gè)字節(jié)來(lái)表示 1 個(gè)英文字符。超出此范圍使用0x80~0xFFFF來(lái)編碼,即擴(kuò)展的ASCII編碼。
不同的國(guó)家和地區(qū)制定了不同標(biāo)準(zhǔn),由此產(chǎn)生了 GB2312、GBK、GB18030、Big5、Shift_JIS 等各自的編碼標(biāo)準(zhǔn)。這些使用多個(gè)字節(jié)來(lái)代表一個(gè)字符的各種漢字延伸編碼方式,稱(chēng)為 ANSI 編碼。在簡(jiǎn)體中文Windows操作系統(tǒng),ANSI 編碼代表 GBK 編碼;在繁體中文Windows操作系統(tǒng),ANSI編碼代表Big5;在日文Windows操作系統(tǒng),ANSI 編碼代表 Shift_JIS 編碼。
以上內(nèi)容摘自百度百科,可以看出,ANSI和ASCII還是有關(guān)系的。ANSI也叫本地碼。
我們要做到能在Unicode、UTF-8、ANSI這三種編碼格式中自由轉(zhuǎn)換。如下圖所示:
在C++中,要怎么做呢?當(dāng)然是用標(biāo)準(zhǔn)庫(kù)的東西啦,C++11對(duì)國(guó)際化標(biāo)準(zhǔn)做得還是可以的,提供了這些接口,正如圖中虛線所示,標(biāo)準(zhǔn)庫(kù)沒(méi)有提供UTF-8到ANSI的互相轉(zhuǎn)化接口,但是我們可以自己封轉(zhuǎn)接口,借用這條路(UTF-8 <=> Unicode <=> ANSI)來(lái)實(shí)現(xiàn)。
因此,接下來(lái)就聊聊UTF8 <=> Unicode和Unicode <=> ANSI。
UTF8 <=> Unicode
------------?strongerHuang?------------
先看代碼:
std::string UnicodeToUTF8(const std::wstring & wstr)
{
std::string ret;
try {
std::wstring_convert< std::codecvt_utf8<wchar_t> > wcv;
ret = wcv.to_bytes(wstr);
} catch (const std::exception & e) {
std::cerr << e.what() << std::endl;
}
return ret;
}
std::wstring UTF8ToUnicode(const std::string & str)
{
std::wstring ret;
try {
std::wstring_convert< std::codecvt_utf8<wchar_t> > wcv;
ret = wcv.from_bytes(str);
} catch (const std::exception & e) {
std::cerr << e.what() << std::endl;
}
return ret;
}
UTF-8是多字節(jié)字符串(multibyte string),而Unicode是寬字符字符串(wchar_t string)。
而C++11提供了wstring_convert這個(gè)類(lèi),這個(gè)類(lèi)可以在wchar_t string和multibyte string之間來(lái)回轉(zhuǎn)換;
而codecvt_utf8可以提供UTF-8的編碼規(guī)則。這個(gè)類(lèi)在#include
從UTF8到Unicode調(diào)用成員函數(shù)wstring_convert::from_bytes;
從Unicode到UTF8調(diào)用成員函數(shù)wstring_convert::to_bytes;
Unicode <=> ANSI
------------?strongerHuang?------------
std::string UnicodeToANSI(const std::wstring & wstr)
{
std::string ret;
std::mbstate_t state = {};
const wchar_t *src = wstr.data();
size_t len = std::wcsrtombs(nullptr, &src, 0, &state);
if (static_cast<size_t>(-1) != len) {
std::unique_ptr< char [] > buff(new char[len + 1]);
len = std::wcsrtombs(buff.get(), &src, len, &state);
if (static_cast<size_t>(-1) != len) {
ret.assign(buff.get(), len);
}
}
return ret;
}
std::wstring ANSIToUnicode(const std::string & str)
{
std::wstring ret;
std::mbstate_t state = {};
const char *src = str.data();
size_t len = std::mbsrtowcs(nullptr, &src, 0, &state);
if (static_cast<size_t>(-1) != len) {
std::unique_ptr< wchar_t [] > buff(new wchar_t[len + 1]);
len = std::mbsrtowcs(buff.get(), &src, len, &state);
if (static_cast<size_t>(-1) != len) {
ret.assign(buff.get(), len);
}
}
return ret;
}
標(biāo)準(zhǔn)庫(kù)提供了wcsrtombs和mbsrtowcs這兩個(gè)函數(shù),當(dāng)然C的標(biāo)準(zhǔn)庫(kù)也提供了這兩個(gè)函數(shù)。
講下wcsrtombs,這個(gè)函數(shù)把寬字符串轉(zhuǎn)成多字節(jié)字符串。編碼規(guī)則受地域的LC_CTYPE影響。因此這個(gè)函數(shù)可以用于本地碼的轉(zhuǎn)化(和本地的編碼息息相關(guān))。
因此,有關(guān)于本地碼的使用,在代碼中要加上下列語(yǔ)句:
setlocale(LC_CTYPE, "");
目的是讓本地碼生效,這條代碼的作用就是讓C++語(yǔ)言的Locale(地域)和本地的地域相同。在Linux下可以運(yùn)行l(wèi)ocale命令看看:
可以看到,LC_CTYPE = en_US.UTF-8,這表示英文,英國(guó),UTF-8編碼,也就是說(shuō)本地碼就是這個(gè)。
當(dāng)然,你也可以在setlocale中指定一些編碼規(guī)則,把wcsrtombs用于別的編碼轉(zhuǎn)化,但是,這里不推薦,因?yàn)閟etlocale是全局的,設(shè)置了這個(gè)就會(huì)影響其他地方的編碼。
wcsrtombs的四個(gè)參數(shù)分別代表什么意思呢?
std::size_t wcsrtombs( char* dst, const wchar_t** src, std::size_t len, std::mbstate_t* ps );
dst,轉(zhuǎn)化后的結(jié)果存入dst指向的內(nèi)存;
src,待轉(zhuǎn)化的字符串的指針的指針;
len,dst指向內(nèi)存的可用字節(jié)數(shù);
ps,轉(zhuǎn)換的狀態(tài),一般默認(rèn)初始化就好了;
return type,轉(zhuǎn)化后結(jié)果的長(zhǎng)度,不包含\0。
注意:如果dst == nullptr,這個(gè)時(shí)候wcstombs的返回值表示會(huì)有這么多字節(jié)的結(jié)果產(chǎn)生,因此,我們可以拿到這個(gè)返回值去新建一個(gè)數(shù)組來(lái)存儲(chǔ)new char[len + 1]。所以,一般調(diào)用兩次wcstombs。
mbsrtowcs同理。
UTF-8 <=> ANSI
------------?strongerHuang?------------
以Unicode為中介裝換便是。
std::string UTF8ToANSI(const std::string & str)
{
return UnicodeToANSI(UTF8ToUnicode(str));
}
std::string ANSIToUTF8(const std::string & str)
{
return UnicodeToUTF8(ANSIToUnicode(str));
}
總結(jié)
------------?strongerHuang?------------
C++11的標(biāo)準(zhǔn)庫(kù)還是挺強(qiáng)大的,雖然這么強(qiáng)大,但是很多特性還不了解,因此還是要多擴(kuò)寬自己的視野,不然有好東西都不知道用,那就棒槌了。
對(duì)了,在Linux下加上setlocale(LC_CTYPE, "")后程序在命令行中可以正常顯示,不加有可能不正常顯示,原因是setlocale(LC_CTYPE, "")也影響了cout,全局的嘛;而在CodeBlocks下不能正常顯示,不知道為什么,但是調(diào)試的過(guò)程中,觀察到了正常的結(jié)果;Visual Studio中沒(méi)有做實(shí)驗(yàn),不過(guò)應(yīng)該沒(méi)問(wèn)題。
來(lái)源:
https://blog.csdn.net/flushhip/article/details/82836867
手把手教你用STM32Trust生成加密固件
HAL庫(kù)中do{} while(0U)宏定義的作用和意義
FreeRTOS如何減少RAM占用空間并加快執(zhí)行速度
長(zhǎng)按前往圖中包含的公眾號(hào)關(guān)注
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!