2015-06-14 48 views
6

考慮下面的代碼:在C編碼的文件名打開UTF8 ++的Windows

#include <iostream> 
#include <boost\locale.hpp> 
#include <Windows.h> 
#include <fstream> 

std::string ToUtf8(std::wstring str) 
{ 
    std::string ret; 
    int len = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0, NULL, NULL); 
    if (len > 0) 
    { 
     ret.resize(len); 
     WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len, NULL, NULL); 
    } 
    return ret; 
} 

int main() 
{ 
    std::wstring wfilename = L"D://Private//Test//एउटा फोल्दर//भित्रको फाईल.txt"; 
    std::string utf8path = ToUtf8(wfilename); 
    std::ifstream iFileStream(utf8path , std::ifstream::in | std::ifstream::binary); 
    if(iFileStream.is_open()) 
    { 
     std::cout << "Opened the File\n"; 
     //Do the work here. 
    } 
    else 
    { 
     std::cout << "Cannot Opened the file\n"; 

    } 
    return 0; 

} 

如果我正在運行的文件,我不能打開這樣的文件進入else塊。即使使用boost::locale::conv::from_utf(utf8path ,"utf_8")而不是utf8path也不起作用。如果我考慮使用wifstream並使用wfilename作爲參數,代碼將起作用,但我不想使用wifstream。有沒有辦法打開它的名字utf8編碼的文件?我正在使用Visual Studio 2010

+1

沒有底層的Windows API使用UTF8。 std :: ifstream最終將調用CreateFileA或CreateFileW來打開該文件,而這些函數的其他功能則採用UTF8。 –

+0

所以,如果我要使用'ifstream'我應該如何改變代碼來使它工作。我應該使用'wstring' – Pant

+0

問題是我試圖讓代碼跨平臺。由於Linux已經知道unicode,所以如果我使用'ifstream',代碼應該可以工作。我應該如何處理這種情況? – Pant

回答

11

在Windows上,MUST使用8位ANSI(和它必須匹配用戶的語言環境)或UTF16的文件名,也可以沒有其他選擇。您可以在主代碼中繼續使用string和UTF8,但在打開文件時必須將UTF8文件名轉換爲UTF16。效率較低,但這是你需要做的。

幸運的是,VC++的實施std::ifstreamstd::ofstream非標準重載它們的構造和open()方法接受wchar_t*字符串UTF-16文件名。

explicit basic_ifstream(
    const wchar_t *_Filename, 
    ios_base::openmode _Mode = ios_base::in, 
    int _Prot = (int)ios_base::_Openprot 
); 

void open(
    const wchar_t *_Filename, 
    ios_base::openmode _Mode = ios_base::in, 
    int _Prot = (int)ios_base::_Openprot 
); 
void open(
    const wchar_t *_Filename, 
    ios_base::openmode _Mode 
); 

explicit basic_ofstream(
    const wchar_t *_Filename, 
    ios_base::openmode _Mode = ios_base::out, 
    int _Prot = (int)ios_base::_Openprot 
); 

void open(
    const wchar_t *_Filename, 
    ios_base::openmode _Mode = ios_base::out, 
    int _Prot = (int)ios_base::_Openprot 
); 
void open(
    const wchar_t *_Filename, 
    ios_base::openmode _Mode 
); 

你將不得不使用一個#ifdef檢測的Windows編譯(不幸的是,不同的C++編譯器識別不同)打開文件時,您的UTF8字符串並暫時轉換爲UTF-16。

#ifdef _MSC_VER 
std::wstring ToUtf16(std::string str) 
{ 
    std::wstring ret; 
    int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0); 
    if (len > 0) 
    { 
     ret.resize(len); 
     MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len); 
    } 
    return ret; 
} 
#endif 

int main() 
{ 
    std::string uft8path = ...; 
    std::ifstream iFileStream(
     #ifdef _MSC_VER 
     ToUtf16(uft8path).c_str() 
     #else 
     uft8path.c_str() 
     #endif 
     , std::ifstream::in | std::ifstream::binary); 
    ... 
    return 0; 
} 

請注意,這隻能保證在VC++中工作。其他用於Windows的C++編譯器不保證提供類似的擴展。

+0

+1這工作。對於那些想要將'utf8'轉換爲'utf16'的人來說,還有另外一個可用的功能[這裏](http://stackoverflow.com/a/7154226/2634612)。 – Pant

+2

有很多可用的UTF轉換實現。手動實現(如鏈接的),Unicode庫,如libiconv和ICU,甚至C++ 11中的std :: codecvt_utf8_utf16。 –

+0

不用在每個打開的文件中都放入'#ifdef',你可以創建一個'filename(const std :: string&fname)'函數,並將所有可疑東西放在一個地方。然後,只需在需要打開文件的文件名上使用該功能。 –