1. 需要關(guān)注的字符編碼
在處理程序的字符編碼問題時,應(yīng)該關(guān)注3個階段的編碼設(shè)置。
-
源代碼的編碼
源代碼本身就是文本文件复唤。在編譯時,編譯器需要知道源代碼的編碼格式烛卧,如果編碼錯誤佛纫,將無法通過編譯。 -
運行時的編碼
程序在處理字符時总放,需要確定字符用什么編碼在內(nèi)存中表示呈宇。 -
終端的編碼
終端在向程序輸入字符時,需要一種編碼來將用戶的輸入轉(zhuǎn)換成二進(jìn)制局雄。反之甥啄,在接受程序的輸出時,也需要將二進(jìn)制反編碼成字符炬搭。
接下來將詳細(xì)說明關(guān)于這三項編碼的設(shè)置蜈漓。
2. 使用UTF-8編寫源代碼
2.1 Visual Studio 的默認(rèn)編碼
Visual Studio 的源代碼保存格式默認(rèn)為ANSI,即“服從系統(tǒng)區(qū)域設(shè)置”尚蝌。中文版Windows的ANSI一般為GB系列編碼迎变,英文版的則是ASCII碼,不同地區(qū)皆有不同飘言。以ANSI保存的文件受系統(tǒng)區(qū)域設(shè)置影響衣形,拷貝到不同區(qū)域設(shè)置的電腦上可能無法打開。
幸運的是,Unicode解決了這個問題谆吴。使用Unicode的文件在任何電腦上都能打開倒源。Unicode有不同實現(xiàn)版本,這里我們將采用最常見的UTF-8格式(關(guān)于Unicode和UTF-8的關(guān)系和其他細(xì)節(jié)句狼,本文不再贅述)笋熬。UTF-8分為帶BOM和不帶BOM的版本。在Windows上最簡單的方式是將源代碼保存為帶BOM的版本腻菇,這樣MSVC編譯器能自動識別胳螟。當(dāng)然,如果你是純粹UTF-8的信仰者筹吐,可以在編譯時加上/utf-8(方法看這里)來使用無BOM的UTF-8糖耸。
2.2 設(shè)置保存編碼
設(shè)置方式非常簡單,在Viusal Studio中丘薛,進(jìn)入文件→高級保存選項嘉竟。修改編碼即可。點擊“添加命令”边苹,在“文件”類別中找到“高級保存選項”命令,添加即可裁僧。
3. 終端編碼設(shè)置
在C++代碼調(diào)用system函數(shù)勾给,執(zhí)行CHCP指令。
system("CHCP 65001");
如此锅知,終端的編碼即被設(shè)為UTF-8。這條代碼執(zhí)行后脓钾,終端中如果出現(xiàn)“Active code page 65001”的提示售睹,則說明設(shè)置成功。
4. Unicode字符的運行時處理
4.1 UTF-8格式的字符串字面量
以 u8 開頭的字符串字面量在程序內(nèi)部以UTF-8格式表示可训。如
u8"你好Unicode!"
這樣的字符串要用 string 類型接收昌妹。
string hello = u8"你好Unicode!"
4.2 UTF-8格式的輸入/輸出流
如果你的終端已設(shè)置成UTF-8的格式,則可以用 string 類型接受由 std::cin 傳入的UTF-8字符握截,向 std::cout 輸出UTF-8字符飞崖。
string input;
std::cin >> input;
std::cout << u8"復(fù)讀機說:" << input << endl;
你可能聽說過還有一種針對 wstring 的輸出流 std::wcout,但個人不建議使用 wcout谨胞,即使是要輸出 wstring固歪。原因有兩點(wcin同理):
- 根據(jù)cpp reference的說明,wcout 不能和 cout 直接混用。
- 不同系統(tǒng)可能要為 wcout 綁定不同的 locale 信息牢裳,這樣十分麻煩逢防,且對可移植性有害。
如果要輸出 wstring蒲讯,建議先將其轉(zhuǎn)換為 string 再輸出到 cout 中忘朝。這并不麻煩,下面有演示判帮。
4.3 多字節(jié)字符串的切分
根據(jù)UTF-8格式的要求局嘁,一個字符用1-4個字節(jié)表示,反映到string中晦墙,就是1-4個char表示一個字符悦昵。這種字符串一般稱為多字節(jié)字符串。多字節(jié)字符串可以勝任各種對字符串的整體操作偎痛,比如拷貝旱捧、連接等。然而在需要遍歷字符踩麦,對字符一一操作時枚赡,就需要進(jìn)行切分。切分后的結(jié)果為wstring谓谦,即寬字符串 類型贫橙。wstring由wchar_t(寬字符)組成,其中每個 wchar_t 只表示一個字符反粥。通過遍歷wstring卢肃,即可對每個寬字符進(jìn)行操作。
C++ 標(biāo)準(zhǔn)庫中的codecvt頭文件提供了多字節(jié)字符串與寬字符串的轉(zhuǎn)換API才顿。他們是
wstring_convert 模板類中的兩個方法莫湘。
-
wstring from_bytes(string)
將string(多字節(jié)字符串)切分為wstring(寬字符串)。 -
string to_bytes(wstring) 和 string to_bytes(wchar_t)
將wstring(寬字符串)或wchar_t(寬字符)轉(zhuǎn)為string(多字節(jié)字符串)郑气。
例如幅垮,將string轉(zhuǎn)為wstring可以用如下代碼。
#include <codecvt>
//......
using namespace std;
string input = u8"我是UTF-8字符串";
wstring_convert<codecvt_utf8<wchar_t>> converter;//一個wstring_convert對象可以多次使用
wstring output = converter.from_bytes(input);
然后尾组,我們可以遍歷wstring忙芒,將每個wchar_t轉(zhuǎn)換成string,再一一輸出至cout(不直接輸出至wcout的原因前文已敘述)讳侨。
for (size_t i = 0; i < output.length(); i++)
cout << converter.to_bytes(output[i]) << endl;
輸出如下
我
是
U
T
F
-
8
字
符
串
這是一個簡單的寬字符/多字節(jié)字符串互相操作的例子呵萨。總體來說跨跨,需要嚴(yán)格區(qū)分字符與字符之間的邊界的操作需要將string轉(zhuǎn)為wstring潮峦,其他情況下一般使用string即可解決問題。