C++字符串挟秤,字符集壹哺,字符轉(zhuǎn)換和各種亂碼原因

1.C++支持的編碼

C++支持1,2,3,4個字節(jié)的字符串,已經(jīng)有了std::string艘刚,std::wstring管宵,std::u8string,std::u16string攀甚,std::u32string一堆的字符串類型啄糙。

類型 字符串表現(xiàn)方式 類型 說明
std::string "hello world" char ANSI
std::wstring L"hello world" wchar_t Unicode
std::u8string u8"hello world" char UTF-8
std::u16string u"hello world" char16_t UTF-16
std::u32string U"hello world" char32_t UTF-32

2.Windows操作系統(tǒng)編碼

Windows操作系統(tǒng)的API包含了Multibyte(ANSI)和Unicode兩種接口,即A和W為后綴的兩套API云稚。其中Multibyte(ANSI)接口能夠處理的字符集隧饼,根據(jù)不同的國家和地區(qū)制定了不同的標(biāo)準(zhǔn),由此產(chǎn)生了 GB2312静陈、GBK燕雁、GB18030诞丽、Big5、Shift_JIS 等各自的編碼標(biāo)準(zhǔn)拐格。這些使用多個字節(jié)來代表一個字符的各種漢字延伸編碼方式僧免,稱為 ANSI 編碼。在簡體中文Windows操作系統(tǒng)中捏浊,ANSI 編碼代表 GBK (CP 936)編碼懂衩;在繁體中文Windows操作系統(tǒng)中,ANSI編碼代表Big5金踪;在日文Windows操作系統(tǒng)中浊洞,ANSI 編碼代表 Shift_JIS 編碼。

3.C/C++庫函數(shù)字符編碼

C/C++庫函數(shù)因?yàn)槭褂昧薟indows API來實(shí)現(xiàn)對操作系統(tǒng)資源的使用胡岔,所以使用字符串字符集的標(biāo)準(zhǔn)和Windows操作系統(tǒng)的要求是一致的法希,比如fopen使用了OpenFile,此時fopen必須傳入GBK編碼的字符串才可以正常打開文件靶瘸。

4.字符轉(zhuǎn)碼

GBK苫亦,UTF8,UNICODE互相轉(zhuǎn)碼是很常見的怨咪,此處使用ICU屋剑,代碼如下:

#ifndef STRING_UTIL_H_
#define STRING_UTIL_H_
#include <string>

std::string unicode2gbk(const std::wstring& ws);
std::wstring gbk2unicode(const std::string& str);

std::wstring utf82unicode(const std::string& str);
std::string unicode2utf8(const std::wstring& ws);

std::string utf82gbk(const std::string& str);
std::string gbk2utf8(const std::string& str);
#endif

#include "string_util.h"
#include <assert.h>
#include <memory.h>
#include <unicode/ucnv.h>
#include <unicode/ustring.h>

#define  BUFFER_SIZE 8192

#ifdef WIN32_MSVC
#ifdef _DEBUG
#pragma comment(lib, "icuucd.lib")
#pragma comment(lib, "icudtd.lib")
#else
#pragma comment(lib, "icuuc.lib")
#pragma comment(lib, "icudt.lib")
#endif
#endif

std::string conv(const std::wstring& ws, const char *converterName)
{
    UErrorCode status = U_ZERO_ERROR;
#ifdef _MSC_VER
    const UChar* source = (const UChar*)ws.c_str();
    int32_t srcLength = ws.length();
#else
    UChar source[BUFFER_SIZE];
    int32_t srcLength = 0;
    u_strFromUTF32(source, BUFFER_SIZE, &srcLength, (const UChar32*)ws.c_str(), ws.length(), &status);
    if (U_FAILURE(status))
    {
        return "";
    }
#endif

    UConverter* converter = ucnv_open(converterName, &status);
    if (U_FAILURE(status))
    {
        return "";
    }

    char buffer[BUFFER_SIZE];
    ucnv_fromUChars(converter, buffer, BUFFER_SIZE, source, srcLength, &status);
    if (U_FAILURE(status))
    {
        ucnv_close(converter);
        return "";
    }
    ucnv_close(converter);


    return buffer;
}

std::wstring conv(const std::string& str, const char *converterName)
{
    UErrorCode status = U_ZERO_ERROR;
    UConverter* converter = ucnv_open(converterName, &status);
    if (U_FAILURE(status))
    {
        return L"";
    }

    UChar dest[BUFFER_SIZE];
    ucnv_toUChars(converter, dest, BUFFER_SIZE, str.c_str(), str.length(), &status);
    if (U_FAILURE(status))
    {
        ucnv_close(converter);
        return L"";
    }
    ucnv_close(converter);


#ifdef _MSC_VER
    return (wchar_t*)dest;
#else
    wchar_t dest32[BUFFER_SIZE];
    int32_t pDestLength = 0;
    u_strToUTF32((UChar32*)dest32, BUFFER_SIZE, &pDestLength, dest, destCapacity, &status);
    if (U_FAILURE(status))
    {
        return L"";
    }

    return dest32;
#endif
}

std::string unicode2gbk(const std::wstring& ws)
{
    return conv(ws, "gb18030");
}

std::wstring gbk2unicode(const std::string& str)
{
    return conv(str, "gb18030");
}

std::wstring utf82unicode(const std::string& str)
{
    return conv(str, "utf-8");
}

std::string unicode2utf8(const std::wstring& ws)
{
    return conv(ws, "utf-8");
}

std::string utf82gbk(const std::string& str)
{
    return unicode2gbk(utf82unicode(str));
}

std::string gbk2utf8(const std::string& str)
{
    return unicode2utf8(gbk2unicode(str));
}

5.沒有亂碼

再做以下測試之前,此處的文本文件字符集代表的是诗眨,編譯器讀取該代碼文件時唉匾,對代碼中的字符串明文使用的字符集。
一般來講辽话,用Visual Stdio新建一個工程肄鸽,然后在代碼里面加入以下代碼,是可以正常運(yùn)行的油啤。

#include <iostream>
using namespace std;

int main()
{
    cout << "你好 世界!" << endl;
    return 0;
}

用Notepad3打開源文件典徘,看到源文件使用的字符集為ANSI(CP-936),即GBK:


圖片.png

因?yàn)槲谋疚募荊BK,字符串是GBK益咬,所以"你好 世界!"被解釋為了GBK逮诲,所以才一切正常。

6.文本文件字符集導(dǎo)致的亂碼

在Notepad3中幽告,選擇文件->編碼->設(shè)置文檔為->UTF-8后保存梅鹦,此時,文本文件字符集變成了UTF8冗锁。

圖片.png

再運(yùn)行一下齐唆,發(fā)現(xiàn),輸出的是亂碼了冻河。

圖片.png

因?yàn)槲谋疚募荱TF8,字符串是GBK箍邮,所以"你好 世界!"被解釋為了UTF8茉帅,所以輸出是亂碼了。
解決方法锭弊,將字符串轉(zhuǎn)為GBK即可

#include <iostream>
#include "string_util.h"

using namespace std;

int main()
{
    cout << utf82gbk("你好 世界!") << endl;
    return 0;
}

7.文本修飾符導(dǎo)致的亂碼

我們先恢復(fù)文件的文本格式堪澎,文件->編碼->設(shè)置文檔為->ANSI,然后修改一下源碼味滞,在字符串前加入u8樱蛤,運(yùn)行,發(fā)現(xiàn)結(jié)果和上段中輸出的亂碼是一樣的剑鞍。

#include <iostream>
using namespace std;

int main()
{
    cout << u8"你好 世界!" << endl;
    return 0;
}

因?yàn)槲谋疚募荊BK,字符串是UTF8昨凡,所以"你好 世界!"被解釋為了UTF8,所以輸出也是亂碼攒暇。
解決方法土匀,將字符串轉(zhuǎn)為GBK即可

#include <iostream>
#include "string_util.h"

using namespace std;

int main()
{
    cout << utf82gbk(u8"你好 世界!") << endl;
    return 0;
}

該處問題其實(shí)很常見子房,比如通過網(wǎng)絡(luò)發(fā)送過來的字符串形用,提取其中的一部分?jǐn)?shù)據(jù)后,用std::string保存的字符串大概率使用的是UTF8編碼证杭,這也直接導(dǎo)致一些底層使用fopen這些標(biāo)準(zhǔn)庫的函數(shù)無法正常處理字符串田度,此處只需要轉(zhuǎn)換為GBK就可以正常使用了。

8.文本文件字符集和文本修飾符同時使用UTF8

這里解愤,我們把文件格式轉(zhuǎn)換為UTF8镇饺,并運(yùn)行以下代碼:

#include <iostream>
using namespace std;

int main()
{
    cout << u8"你好 世界!" << endl;
    return 0;
}

發(fā)現(xiàn),亂碼和前兩段中的例子輸出不一樣送讲。


圖片.png

這里其實(shí)設(shè)計(jì)到的是雙重轉(zhuǎn)碼的問題奸笤,即字符串被字符串修飾符u8轉(zhuǎn)碼一次,再被文本文件轉(zhuǎn)碼一次哼鬓。所以此處修正方法如下:

#include <iostream>
#include "string_util.h"

using namespace std;

int main()
{
    cout << utf82gbk(utf82gbk(u8"你好 世界!")) << endl;
    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末监右,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子异希,更是在濱河造成了極大的恐慌健盒,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件称簿,死亡現(xiàn)場離奇詭異扣癣,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)憨降,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門父虑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人授药,你說我怎么就攤上這事士嚎〈寡模” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵航邢,是天一觀的道長耕赘。 經(jīng)常有香客問我,道長膳殷,這世上最難降的妖魔是什么操骡? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮赚窃,結(jié)果婚禮上册招,老公的妹妹穿的比我還像新娘。我一直安慰自己勒极,他們只是感情好是掰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辱匿,像睡著了一般键痛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上匾七,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天絮短,我揣著相機(jī)與錄音,去河邊找鬼昨忆。 笑死丁频,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的邑贴。 我是一名探鬼主播席里,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拢驾!你這毒婦竟也來了奖磁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤独旷,失蹤者是張志新(化名)和其女友劉穎署穗,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嵌洼,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡案疲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了麻养。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褐啡。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鳖昌,靈堂內(nèi)的尸體忽然破棺而出备畦,到底是詐尸還是另有隱情低飒,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布懂盐,位于F島的核電站褥赊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏莉恼。R本人自食惡果不足惜拌喉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俐银。 院中可真熱鬧尿背,春花似錦、人聲如沸捶惜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吱七。三九已至汽久,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間陪捷,已是汗流浹背回窘。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工诺擅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留市袖,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓烁涌,卻偏偏與公主長得像苍碟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子撮执,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容