c 字串的編碼?

2021-09-25 14:00:06 字數 3556 閱讀 5285

c++ 字串載入到記憶體裡面是什麼編碼格式的?

win7中文系統下,控制台預設是gbk編碼的,用gbk格式儲存的原始檔,中文字串在vs2010下編譯輸出到控制台會正常輸出

但是vs2010裡面採用utf-8無bom的原始檔 輸出中文字串到終端就出現亂碼了

所以 是不是c++把字串的值載入到記憶體中時 是按照cpp檔案的編碼儲存的? 也就是說utf-8編碼的cpp檔案,編譯後字串載入到記憶體時是utf-8編碼的?

這個問題想要說清楚還是挺複雜的,題主可以參考 new options for managing character sets in the microsoft c/c++ compiler 這篇文章。具體來說,源**檔案中的字串常量能否正確的顯示在控制台視窗中與以下幾個因素有關:

第一,「源**檔案儲存時所使用的字符集」決定了源**檔案中的抽象字元儲存到硬碟中是什麼樣的位元組。這裡所說的抽象字元,就是指人所能識別的文字(如「張」)。而位元組則是由抽象字元按照字符集的規定對映的,不同的字符集對映的結果不一樣(如「張」在 utf-8 下對映的位元組是e5 bc a0三個位元組,而在 gbk 下對映的位元組是d5 c5兩個位元組)。比如下面一行**:

char *s = "張三";
其中的字串常量"張三"在使用不同的字符集(c1)儲存時,對映的位元組是不同的。

第二,「編譯器在讀取源**檔案時所使用的內部字符集」決定了編譯器如何把讀入的源**檔案位元組流進行轉換。我所謂的轉換就是指把位元組流從一種編碼轉到另一種編碼。舉例來說,對於抽象字元「張」,如果採用 gbk 編碼,對映的位元組流就是d5 c5,而轉換到 utf-8 編碼,就是把這個位元組流轉換成e5 bc a0

這個字符集(c2)是由編譯器決定的,標準中並未規定。不同的編譯器可能採用不同的內部字符集,同樣的編譯器不同版本也可能採用不同的內部字符集。在 visual c++ 的某些版本中,內部字符集是 utf-8,編譯器會嘗試判斷原始檔所使用的字符集(c1),並將其轉換成內部字符集(c2),如果編譯器不能判斷出檔案所使用的字符集,則會預設其(c1)為當前作業系統的**頁(default code page)(這裡如果判斷錯誤,就會造成亂碼或編譯出錯)。

比如:原始檔如果是 utf-8 with bom,則能夠正確的被 visual c++ 識別,其中的抽象字元「張」對映的位元組流e5 bc a0就會被正確的轉換成e5 bc a0(沒變)。而原始檔如果是 utf-8 without bom,則不能正確的被 visual c++ 識別,編譯器會採用當前的**頁來進行轉換,其中的抽象字元「張」對映的位元組流e5 bc a0就會被當作 gbk 編碼,錯誤的轉換成其它位元組流e5 af ae ...(寮)。

第三,「編譯器在編譯時所使用的字符集」決定了編譯器如何把源**中使用內部字符集(c2)編碼的字元/字串常量轉還到編譯時所使用的字符集(c3)。這個字符集(c3)也是由編譯器決定的,標準中並未規定。c++ 中的字元常量和字串常量有不同的型別,它們對應於不同的 c3。

在 visual c++ 中,參考 string and character literals 和前文提到的部落格,可以推知不同型別的字元/字串常量對應的 c3:

// character literals

auto c0 = 'a'; // char, encoded as default code page

auto c1 = u8'a'; // char, encoded as utf-8

auto c2 = l'a'; // wchar_t, encoded as utf-16le

auto c3 = u'a'; // char16_t, encoded as utf-16le

auto c4 = u'a'; // char32_t, encoded as utf-32le

// string literals

auto s0 = "hello"; // const char*, encoded as default code page

auto s1 = u8"hello"; // const char*, encoded as utf-8

auto s2 = l"hello"; // const wchar_t*, encoded as utf-16le

auto s3 = u"hello"; // const char16_t*, encoded as utf-16le

auto s4 = u"hello"; // const char32_t*, encoded as utf-32le

編譯器根據字串常量的型別把其從 c2 轉換到 c3(前面如果判斷錯誤,這裡就會繼續保留錯誤)。

比如:auto s1 = "張";,抽象字元「張」在 c2 中(utf-8)對映的位元組流e5 bc a0就會被轉換成在 c3 中(cp936,gbk)對映的位元組流d5 c5

auto s2 = u8"張";,抽象字元「張」在 c2 中(utf-8)對映的位元組流e5 bc a0就會被轉換成在 c3 中(utf-8)對映的位元組流e5 bc a0(不變)。

第四,「執行可執行程式的控制台視窗所使用的字符集」決定了如何把編譯好的可執行程式中的位元組流轉換成抽象字元顯示在控制台中。比如,在上一步中的s1對映的位元組流就會通過 c4(cp 936)對映回抽象字元「張」,在我們看來就是正確的。而上一步中的s2對映的位元組流就會通過 c4(cp 936)對映回抽象字元「寮」,在我們看來就是亂碼。

以上,就是我理解的 c++ 中字元/字串的編碼處理方式,如果有誤還請指出:-)

題主可以嘗試在 visual c++ 中把以下**分別儲存成 cp936、utf-8 with bom、utf-8 without bom 的格式,看看輸出結果是什麼。

#include #include using namespace std;

int main()

ofstream os2("s2.txt");

if (os2.is_open())

ofstream os3("s3.txt");

if (os3.is_open())

cin.get();

return 0;

}

輸出的三個檔案中,前兩個檔案s1.txts2.txt都能夠被正常的文字編輯器猜出其編碼格式,從而正確顯示內容,但是第三個檔案s3.txt會顯示成部分或全部亂碼,因為其中既包含了 utf-8 編碼的位元組流又包含了 gbk 編碼的位元組流,所以文字編輯器就不知道該用什麼編碼來把位元組流對映回抽象文字了。

字串編碼

1.unicode 的編碼方式 編碼類似1小時和60分鐘的關係,本質的時間刻度還是相同的。unicode 編碼有 utf 8 utf 16 和 utf 32 它們都是將數字轉換到程式資料的編碼方案。utf 8 以位元組為單位。表示乙個字元時,能用乙個位元組就不用兩個或者三個位元組表示。utf 16 ...

C 如何改變字串編碼

public string utf8togb2312 string str catch exception ex unsupportedencodingexception ex public string gb2312toutf8 string str byte temp1 encoding.con...

C 如何改變字串編碼

public string utf8togb2312 string str catch exception ex unsupportedencodingexception ex public string gb2312toutf8 string str byte temp1 encoding.con...