0

如何比較字符(char)或字符串(char*std::stringstd::wstring,等等),這是在不同的運行環境中可以安全地進行本地化和改變字符編碼?比較字符串/字符在本地化與相應的文字這樣的字符串/字符用C字面++安全的方式

讓我們以下面的最小例子作爲開始。

using namespace std; 
// Get runtime locale and apply it to i/o streams 
locale loc(""); 
cout.imbue(loc); 
cin.imbue(loc); 

// Ask question and compare answer 
char c = '\0'; 
do { 
    cout << "Important question [y/n]" << endl; 
    cin >> c; 
} while(c != 'n' && c != 'y'); 

if(c == 'n') { 
    // execute 'no'-branch 
} else { 
    // execute 'yes'-branch 
} 

(我知道的例子可以在許多方面得到改善。輸入流應該讀取下一個字符之前被清除等。但在這裏,這是不是問題的關鍵。)

我的問題是將來自環境的字符c與硬編碼字面值'n'進行比較,儘管變量類型的名稱爲char但我們實際上並未比較字符(或字符),而是按比特級別比較單個字節。

在編譯期間,文字'n'被轉換爲執行字符集。如果編譯器在Linux下是gcc,則默認爲UTF-8。但是這不能保證,因爲標準只需要一個覆蓋特定字符的代碼集。所以實際上每個編譯器都可以自由選擇合適的字符集。但無論如何,假設'n'被編譯器翻譯爲'\x6e'片刻。

但是,運行時環境可以使用完全不同的編碼。假設環境使用UTF-16。如果用戶鍵入「n」,則輸入流將填充兩個字節序列"\x00\x6e"。其中,cin >> c讀取第一個字節'\x00'並將其與'\x6e'進行比較。顯然,這不是打算的。

此外,如果我們想要將字符串拆分爲標記,情況會變得更糟。有幾個功能(C的strtok,boost::tokenize),但基本上他們都做什麼strtok。他們採用一個輸入字符串和一串字符作爲分隔符號。但是,這些函數不能在字符上工作,而是在字節上工作。

讓我們這個簡單的例子

strtok("alice, bob; charlie", ",;"); 

意向的第一個字符串應該被拆分無論是「」或‘’。此外,讓我們假設通過一些未知的奇蹟,兩個字符串幸運地由相同的字符編碼UTF-16編碼。雖然兩個字符串具有相同的編碼,但結果是總損失,因爲",;"是四字節序列"\x00\x2c\x00\x3b",第一個字符串是40字節序列,每個第二個字節爲'\x00'。因爲strtok(和boost::tokenize和其他)在字節上工作,結果是僞造的。

我知道還有std::wstring,因爲C++ 11還有std::u16stringstd::u32string,但它們並不是真正的救援。 (我不想詳細說明它們,因爲這個問題已經足夠長了。)

當然,像IBM的ICU或像Qt這樣的完整框架等第三方庫可以避免所有這些問題,但所有這些庫都解決了這些問題通過定義自己的字符串類來解決問題。

一方面,這些庫大多互相不兼容,或者如果想要合併這些庫,必須進行大量的類型轉換和字符串複製。另一方面,我通常只寫小命令行工具,我不想創建一個像Qt這樣的真正龐大的庫的依賴,只是爲了完成像這個問題的第一個例子那樣的任務。

我不能相信,像一個字符與'y''n'比較這樣一個瑣碎的問題沒有「標準」的解決方案,只使用C++標準庫。所以回到我原來的問題:

如何比較字符(char)或字符串(char*std::stringstd::wstring,等)與相應的文字,使得它是安全的定位和不同的字符編碼中不同的運行時間環境對其他庫的依賴性很小

+0

「我不能相信,對於比較字符'y'或'n'這樣的小問題,沒有」標準「的解決方案,只使用C++標準庫。」 - 不幸的是,情況正是如此。 – dgel

回答

0

您可以在文本模式下打開窄字符流,讀取一些字符並將它們與文字進行比較。而已。它的工作原理根據定義。根據您的實施定義,等於'n'的字符爲'n'

什麼保證'n'字符,您的實施理解是ASCII n或EBCDIC n或什麼?沒有。執行字符集與環境使用的字符集之間的映射是實現定義的。該映射可能與語言環境有關,因此您可以通過設置適當的語言環境來選擇幾種映射。你需要查閱你的實現文檔,或者只是盲目地相信(下半部分)映射是由ASCII給出的。幸運的是,理智的實現提供了一個映射,JustWorks ™,瘋狂的實現不能長存。

關於與一個UTF-16串的例子,除非實現承諾,其字符集映射1:1至UTF-16或其一部分(在一些區域設置),可以使用wchar_t的和適當的區域設置,或(自C++ 11以來)char16_t和u16文字。這基本上是他們的目的。

+0

我是否正確理解您要聲明'cin >> c'已經執行從運行時字符集到內部字符集的轉換,以便'c =='n''做正確的事情?但是,如果這種隱式轉換失敗會發生什麼問題?例如,因爲用戶輸入了一個不適合單個「char」的字符?在這種情況下,「cin >> c」是否引發異常? – user2690527

+0

沒有「內部字符集」和「運行時字符集」,只有一個執行字符集。除非使用自定義的'codecvt'方面,否則不存在字符到字符的轉換。微軟的實施可能會或可能不會有不同的表現。無論如何,基本執行字符集的成員都不是與語言環境相關的。 –