2017-04-12 493 views
1

我有一個文件,行結束符是windows的風格\r\n;它被編碼在USC-2的小端中。如何正確轉換USC-2的小端到UTF-8?

說這是我的文件fruit.txt(USC-2小端):

input file

於是我在一個std::wifstream打開它,並嘗試分析內容:

// open the file 
    std::wifstream file("fruit.txt"); 
    if(! file.is_open()) throw std::runtime_error(std::strerror(errno)); 

// create container for the lines 
    std::forward_list<std::string> lines; 

// Add each line to the container 
    std::wstring line; 
    while(std::getline(file,line)) lines.emplace_front(wstring_to_string(line)); 

如果我嘗試打印到cout ...

// Printing to cout 
    for(auto it = lines.cbegin(); it != lines.cend(); ++it) 
     std::cout << *it << std::endl; 

...這就是它輸出:

Cherry 
Banana 
ÿþApple 

更糟糕的是,如果我在記事本++打開它,這就是它看起來像

Null characters everywhere

我可以排序,通過整治強制轉換的編碼返回到USC-2,這導致這樣的:

enter image description here

wstring_to_string功能被定義爲:

std::string wstring_to_string(const std::wstring& wstr) { 
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert; 
    return convert.to_bytes(wstr); 
} 

這個世界正在發生什麼?我怎樣才能得到一個正常的UTF-8字符串?我也嘗試過這種方法:How to read utf-16 file into utf-8 std::string line by line,但是填充std::wifstream首先導致完全沒有輸出。有人能幫助指導我以最好的方式將USC-2 LE數據轉換爲可讀的UTF-8數據嗎?

編輯我認爲可能是由MSYS2提供的mingw64/mingw-w64-x86_64-gcc 6.3.0-2的一個bug。我已經嘗試過所有人的建議,並將語言環境嵌入到流中,只是根本沒有輸出。我知道只有兩個本地語言環境,「C」和「POSIX」。我打算嘗試Visual Studio,但沒有足夠的網絡速度用於4GB下載。我曾使用過ICU,像@Andrei R.建議的那樣,它工作得很好。

我會喜歡使用標準庫,但我確定這一點。如果您需要此解決方案,請查看我的代碼:https://pastebin.com/qudy7yva

+0

這是Windows嗎?通過將控制檯文本複製到編輯器,您獲得了NP ++圖片嗎? (在有人說NP ++是Windows程序之前,它在Wine上運行良好) – deviantfan

+0

是的,這是Windows。通過運行我的程序來獲得log.txt,如下所示:./program.exe> log.txt。我使用的是MSYS2的g ++ 6.3.0 –

+1

那麼你應該知道Windows控制檯(對於所有版本的Windows)都不能處理UTF8。有些東西可以直接使用,有些東西有解決方法,但100%正確的行爲是不可能的(例如,由於一些CRT錯誤,他們無意修復(因爲工作太多))。 >重定向不是你自己程序的一部分,所以我不會太依賴它。 – deviantfan

回答

0

轉換爲/從unicode通常不是那麼平凡。看看ICU庫,我相信這是迄今爲止最完整的c/C++編碼轉換庫。

也有平臺依賴的方式,如WideCharToMultibyte (Win)iconv (Linux)。或者,使用Qt,您可以使用QString::fromUtf16。可能你必須自己扭轉排列順序。

+1

'轉換成unicode/unicode通常不是那麼簡單.'這是一個轉換從Unicode到Unicode ......無需ICU即可管理 – deviantfan

1

代碼本身很好。

真正的問題是您的輸入文件不是有效的UTF-16LE開頭(您使用的std::codecvt_utf8_utf16需要UTF-16,而不是UCS-2)。這清楚地顯示在Notepad ++屏幕截圖中。

副手,文件數據看起來像一個BOM一個UTF-16LE文件(ÿþ是UTF-16LE BOM作爲8位ANSI觀察時)被所附原樣到UCS-2BE的端部(或UTF-16BE)文件沒有BOM。

您需要修復輸入文件,以便整個文件從開始到結束(有或沒有前面的BOM,而不是中間)都是有效的UTF-16LE。

然後你已經有的代碼將工作。

+1

'真正的問題是您的輸入文件不是......可以清晰地顯示在您的Notepad ++屏幕截圖中。我認爲截圖來自輸出。 – deviantfan

+1

我用'fruit.txt'的屏幕截圖編輯了我的答案, –

+0

...和新的屏幕截圖(這次輸入)看起來不錯。 – deviantfan

0

對於您的情況,主要問題是您使wifstream以錯誤的方式讀取文件。如果你在wstring_to_string中打印wstr的大小,你會發現它不是你所期望的。

https://stackoverflow.com/a/19698449/4005852

設置正確的語言環境會解決這個問題。

std::string wstring_to_string(const std::wstring& wstr) { 
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert; 
    return convert.to_bytes(wstr); 
} 

int main() 
{ 
// open the file 
    std::wifstream file("fruit.txt", std::ios::binary); 
    file.imbue(std::locale(file.getloc(), 
      new std::codecvt_utf16<wchar_t, 0x10ffff, std::little_endian>)); 
    if(! file.is_open()) throw std::runtime_error(std::strerror(errno)); 

// create container for the lines 
    std::forward_list<std::string> lines; 

// Add each line to the container 
    std::wstring line; 
    file.get(); // remove BOM 
    while(std::getline(file,line)) lines.emplace_front(wstring_to_string(line)); 

// Printing to cout 
    for(auto it = lines.cbegin(); it != lines.cend(); ++it) 
     std::cout << *it << std::endl; 

    return 0; 
} 
+0

我根本沒有輸出。我開始認爲這是一個編譯器錯誤:/ –

+0

我正在使用「Microsoft(R)C/C++ Optimizing Compiler Version 19.00.24210 for x64」。你的編譯器是什麼? –

+0

好的。我目前正在下載Visual Studio以嘗試使用另一個編譯器。我通常使用MSYS2的g ++ –