2017-02-22 77 views
1

我已經花了很多時間閱讀有關Unicode,編碼和許多相關主題。
我研究背後的原因是因爲我試圖讀取文件的內容並逐個字符地解析它們。C++正確讀取unicode字符可能大於一個字節的文件

糾正我,如果我錯了,請:

C++的getc()返回int可能等於EOF
如果返回值不等於EOF它可以是 解釋爲 安全地分配給char
由於std::string基於char我們可以使用這些字符構建std::string s並使用這些字符。

我有一個C#的背景,我們使用C#的char(16bit)爲string s。
這些char的值直接映射到unicode值。
A char其值爲5等於位於U+0005的unicode字符。

我不明白的是如何讀取C++中包含字符的值可能大於一個字節的文件。當我只能讀取其值被限制爲一個字節的字符時,我覺得使用getc()並不舒服。

我可能會錯過如何正確讀取C++文件的重要一點。
任何見解都非常感謝。

我使用vC++運行Windows 10 x64。
但我寧願保持這個問題平臺independend如果可能的話。

編輯

我想強調的Klitos Kyriacou的評論鏈接堆棧溢出後(?):
How well is Unicode supported in C++11?

這是一個快速下潛到如何壞的Unicode支持在C++中。
有關更多詳細信息,請閱讀/查看接受答案中提供的資源。

+0

你有機會看看'std :: wstring'和/或'wchar_t'嗎? –

+2

你想使用什麼編碼? –

+0

@VadaPoché讓我讀這些東西了... –

回答

0

我推薦看Unicode in C++ by James McNellis
這將有助於解釋在處理Unicode時,C++具有和不具備哪些功能。
您將看到C++缺乏對使用UTF8輕鬆工作的良好支持。

因爲它聽起來像你想遍歷每個字形(不只是代碼點),
我會推薦使用第三個pary庫來處理錯綜複雜。
utfcpp對我來說效果不錯。

+0

您所鏈接的討論提供了一些關於C++中的Unicode支持的重要見解。我可以將它推薦給任何想要更好地理解字符編碼(不僅是C++)的人。我將使用utfcpp,因爲從我所瞭解的情況來看,它似乎爲1)驗證和2)轉換提供了最好的功能。 –

2

與Windows API兼容的16位「字符」的等效項將爲wchar_t。請注意,儘管wchar_t在某些平臺上可能爲32位,但如果要以獨立於平臺的方式存儲UTF-16編碼的字符串,請使用char16_t。

如果您在Windows平臺上使用char16_t,則必須在將字符串傳遞給OS API時進行一些強制轉換。

等效串類型是:

  • std::wstring(wchar_t的)
  • std::u16string(char16_t)

文件流類型:

  • std::wifstream(爲std::basic_ifstream<wchar_t>一個typedef)
  • std::basic_ifstream<char16_t>
  • std::wofstream(對於std::basic_ofstream<wchar_t>一個typedef)
  • std::basic_ofstream<char16_t>

實施例來讀取UTF-8編碼的文件爲UTF-16的字符串:

#include <windows.h> 
#include <fstream> 
#include <string> 
#include <locale> 
#include <codecvt> 

int main() 
{ 
    std::wifstream file(L"test_utf8.txt"); 

    // Apply a locale to read UTF-8 file, skip the BOM if present and convert to UTF-16. 
    file.imbue(std::locale(file.getloc(), 
     new std::codecvt_utf8_utf16<wchar_t, 0x10ffff, std::consume_header>)); 

    std::wstring str; 
    std::getline(file, str); 

    ::MessageBox(0, str.data(), L"test", 0); 

    return 0; 
} 

如何將UTF-16編碼文件讀取到16位std::wstringstd::u16string

顯然這並不容易。 有std::codecvt_utf16,但是當與16位wchar_t字符類型一起使用時,它會生成UCS-2,它只是UTF-16的一個子集,所以代理對不會被正確讀取。請參閱cppreference example

我不知道C++ ISO委員會是如何得出這個決定的,因爲它在實踐中完全沒用。至少他們應該提供一個標誌,以便我們可以選擇是否將自己限制在UCS-2或想要閱讀完整的UTF-16範圍。

也許有另一種解決方案,但現在我不知道它。

+0

+1由於多種原因:指出我認爲不應該用於UTF8的wstring和wchar_t,描述不同的文件流併爲UTF8和UTF16提供示例。我不接受這個答案,因爲我相信像utfcpp這樣的庫(由Trevor提到)可以比我能理解/寫的任何樣本更好地處理驗證和解碼。儘管如此,如果有人正在尋找自己寫的解決方案,這可能是要走的路。感謝信息zett42 :) –

+0

不幸的是,我不得不刪除UTF-16的例子,因爲它只讀取UCS-2(見上文)。應仔細閱讀文檔... – zett42

+0

只需將文件作爲二進制文件讀取即可。使用標準圖書館的機器,它可以毫不費力地工作,在其他地方做些別的事情。有時過去,我的「做別的事」包括從頭開始編寫UTF-8編碼解碼器,但現在使用C++ 11及更高版本,庫在這方面還不夠充分,以證明這一努力的合理性。 –

1

情況是,C的getc()寫在20世紀70年代。出於所有意圖和目的,它的意思是「讀一個八位字節」,而不是「讀一個字符」。幾乎所有的二進制數據都是建立在八位組上的。

Unicode允許字符超出八位位組可以表示的範圍。所以,天真地,Unicode人提出了一個16位字符的標準。 Microsoft隨後將提案併入其中,並在Windows中添加了寬字符(wchar_t等)。一個問題是,16位不足以表示每種人類語言中的每個字形都具有某種地位,另一個是二進制文件的字節序性。因此,Unicode人員必須添加一個32位的unicode標準,然後他們在Unicode文件的開頭添加了一點enianness和格式標記。最後,16位Unicode字形與微軟的wchar_t字形並不完全匹配。

所以結果是一團糟。閱讀和顯示16位或32位Unicode文件是非常困難的,並且具有完全的準確性和可移植性。而且,很多程序仍然使用8位ASCII碼。

幸運的是,UTF-8發明了。UTF-8向後兼容7位ascii。如果最高位被設置,那麼字形就被多個字符編碼,並且有一個方案告訴你多少個字符。除了作爲字符串結束指示符外,nul字節不會出現。所以大多數程序會正確處理UTF-8,除非他們試圖拆分字符串或以其他方式嘗試將它們視爲英語。

由於可變長度規則,UTF-8具有不可能隨機訪問字符的懲罰。但這是一個小缺點。一般來說,UTF-8是保存Unicode文本並在程序中傳遞它的方法,當你真正需要這些字形時,你應該只把它分解成Unicode碼點。用於顯示目的。

+0

+1提供歷史記錄。還應該說UTF-16非常容易出錯,因爲即使開發人員不知道代理對,它也可以工作99%,因爲這些開發人員最喜歡只用UCS-2範圍內的代碼點進行測試。 – zett42

+0

* UTF-8的懲罰是隨機訪問字符是不可能的* ...對於UTF-16甚至UTF-32也是如此,因爲[抽象字符](https://en.wikipedia.org/wiki/Unicode#Abstract_characters)可以由多個unicode字符組成。 – zett42