2011-07-22 96 views
10

您可以在C++ 11中分別使用字符串前綴u8/u/U來編寫UTF-8/16/32字符串文字。編譯器如何解釋這些新類型的字符串文字中包含非ASCII字符的UTF-8文件?我知道標準沒有指定文件編碼,單單這一事實就會使源代碼中的非ASCII字符的解釋完全不確定的行爲,使得該功能不那麼有用。文件編碼如何影響C++ 11字符串文字?

我知道您仍然可以使用\uNNNN轉義單個unicode字符,但是對於通常包含多個unicode字符的俄文或法文句子來說,這樣的讀法不太可讀。

我從各種來源得知,u應該等效於當前Windows實現上的L,以及上的例如L。 Linux實現。因此,考慮到這一點,我也想知道所要求的行爲是什麼舊字符串文字修飾...

的代碼樣本猴子:

string utf8string a = u8"L'hôtel de ville doit être là-bas. Ça c'est un fait!"; 
string utf16string b = u"L'hôtel de ville doit être là-bas. Ça c'est un fait!"; 
string utf32string c = U"L'hôtel de ville doit être là-bas. Ça c'est un fait!"; 

在一個理想的世界中,所有的這些字符串會產生相同的內容(如轉換後的字符),但是我使用C++的經驗告訴我,這是絕對實現定義的,可能只有第一個會按照我的意願來做。

回答

7

在GCC,使用-finput-charset=charset

設置輸入字符集,從字符集輸入文件到GCC使用源代碼字符集的用於翻譯。如果語言環境未指定,或GCC無法從語言環境獲取此信息,則默認爲UTF-8。這可以通過語言環境或此命令行選項來覆蓋。目前,如果存在衝突,則命令行選項優先。字符集可以是系統的「iconv」庫例程支持的任何編碼。

還檢查了選項-fexec-charset-fwide-exec-charset

最後,關於字符串文字:

char  a[] = "Hello"; 
wchar_t b[] = L"Hello"; 
char16_t c[] = u"Hello"; 
char32_t d[] = U"Hello"; 

字符串文字(LuU)的大小調節劑僅確定類型字面的

+1

你需要在這些文字前加上一個'const'。 –

+6

@尼科爾號甚至假設你的意思是宣佈的變量,不。 –

+2

@尼科爾:爲什麼是誰? 'char x [] =「a」; x [0] = b;' –

4

編譯器必須如何解釋這些新字符串文字類型中包含非ASCII字符的UTF-8文件。我知道標準沒有指定文件編碼,單單這一事實就會使源代碼中的非ASCII字符的解釋完全不確定的行爲,使得該功能不那麼有用。翻譯的

從n3290,2.2階段[lex.phases]

物理源文件中的字符被映射,在一個 實現定義的方式,基本源字符集 (引入新 - 如果需要 ,則爲行結束指示符的在線字符)。接受的物理源文件字符集是實現定義的 。 [這裏有一些關於trigraphs的信息。]任何來源 不在基本源字符集(2.3)中的文件字符被指定該字符的通用字符名稱替換爲 。 (一個 實現可以使用任何內部編碼,只要在源文件中遇到的實際 擴展字符和在源文件中表達的相同 擴展字符作爲 通用字符名稱(即使用\ uXXXX符號),被 等效處理,除非這種替換是在 原始字符串字面回覆。)

有被使用了很多標準術語來描述如何與編碼實現交易。這是我嘗試在爲有些簡單,一步一步發生了什麼說明:

物理源文件字符映射,在 實現定義方式,基本來源字符集[...]

文件編碼的問題是手工編制的;該標準只關心基本的源代碼字符集並留下實現的空間。

在基本源字符集(2.3)的任何源 文件字符由通用字符名稱,其指定該字符替換 。

基本的源碼集是一個簡單的允許字符列表。 這不是ASCII(請參閱更多)。任何不在此列表中的內容都會「轉化」(至少在概念上)爲\uXXXX表單。

所以無論使用哪種字面或文件編碼,源代碼都會在概念上轉換爲基本字符集+一堆\uXXXX。我在概念上說,因爲這些實現實際上做的通常比較簡單,因爲他們可以直接處理Unicode。重要的部分是標準所稱的擴展字符(即不是基本源集)應與其等效的\uXXXX表單區分開使用。請注意,C++ 03可在例如EBCDIC平臺,所以你在ASCII方面的推理是有缺陷的。

最後,我描述的過程也發生在(非原始)字符串文字上。這意味着你的代碼就相當於好像你已經寫了:

string utf8string a = u8"L'h\u00F4tel de ville doit \u00EAtre l\u00E0-bas. \u00C7a c'est un fait!"; 
string utf16string b = u"L'h\u00F4tel de ville doit \u00EAtre l\u00E0-bas. \u00C7a c'est un fait!"; 
string utf32string c = U"L'h\u00F4tel de ville doit \u00EAtre l\u00E0-bas. \u00C7a c'est un fait!"; 
+0

這很有趣。 u8'文字中的'\ u00F4'實際上是否擴展爲兩個字節? –

+0

@Kerrek我對我的實現進行了測試,一個''\ u8XXXX''確實可以大小超過兩個。我沒有引用這個標準,因爲我不確定在哪裏看超越「以u8開頭的字符串字面值,如u8」asdf「,是一個UTF-8字符串字面值,並用給定字符初始化爲以UTF-8編碼「。 (來自2.14.5字符串文字[lex.string],第7段)。這可能很容易成爲一個單獨的問題。 –

+1

即使微弱的'U + F4'在UTF-8中已經是兩個字節 - 這非常酷,我沒有意識到在新的C++中實際上有真正的UTF支持(除了提供數據類型之外)。太好了!如果你通過'\ U0010FFFF',在'utf16string'中會發生什麼? –

0

原則,編碼的問題,只有不管你什麼時候輸出你的字符串,使它們對人類可見,這是不如何編程的問題語言被定義,因爲它的定義只涉及編碼計算。所以,當你決定時,你在編輯器中看到的內容是否與你在輸出中看到的一樣(任何類型的圖像,不管是在屏幕上還是在pdf中),你應該問自己哪種習慣方式您的用戶交互庫和您的操作系統編碼假設。 (例如,這裏的信息for Qt5:在Qt5中,你看到的是應用程序的用戶,以及你的程序員看到的是什麼,如果QStrings的老式字符串文本的內容被編碼爲utf8在你的源文件中,除非你在應用程序執行過程中打開另一個設置)。

作爲一個結論,我認爲Kerrek SB是正確的,Damon是錯誤的:實際上,在代碼中指定文字的方法應該指定它的類型,而不是源文件中用於填充它的編碼內容,因爲文字的類型是關於計算所做的事情。像u"string"這樣的東西只是一個「unicode codeunits」(即char16_t類型的值)的數組,無論操作系統或任何其他服務軟件稍後對他們做了什麼,然而他們的工作都會爲您或其他用戶尋找。你只需要爲自己添加另一個約定的問題,即在計算中的數字的「含義」(即它們提供Unicode的代碼)和它們在屏幕上的表示形式之間的對應關係,就像在你的文本編輯器中工作一樣。作爲一名程序員如何以及是否使用這個「含義」是另一個問題,並且如何執行這個其他對應關係自然會被實現定義,因爲它與編碼計算無關,只與工具的使用的舒適性有關。