Открыть кодированное имя файла 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 encoded? Я использую Visual Studio 2010.

Ответ 1

В Windows вы ДОЛЖНЫ использовать 8-битный ANSI (и он должен соответствовать языку пользователя) или UTF16 для имен файлов, другой доступной опции нет. Вы можете продолжать использовать string и UTF8 в своем основном коде, но вам придется конвертировать имена файлов UTF8 в UTF16 при открытии файлов. Менее эффективно, но это то, что вам нужно сделать.

К счастью, реализация VC++ std::ifstream и std::ofstream имеет нестандартные перегрузки своих конструкторов и методов open() для принятия строк wchar_t* для имен файлов UTF16.

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 в UTF16 при открытии файла.

#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 utf8path = ...;
    std::ifstream iFileStream(
        #ifdef _MSC_VER
        ToUtf16(utf8path).c_str()
        #else
        utf8path.c_str()
        #endif
        , std::ifstream::in | std::ifstream::binary);
    ...
    return 0;
}

Обратите внимание, что это гарантированно работает только в VC++. Другие компиляторы C++ для Windows не гарантируют аналогичных расширений.