Поддержка UTF-8 в Visual Studio 2017 std:: эксперимент:: файловая система:: путь

Я был рад видеть добавление поддержки для std::experimental::filesystem в Visual Studio 2017, но только сейчас столкнулся с проблемами с Unicode. Я как бы слепо предполагал, что могу везде использовать строки UTF-8, но не получилось - при создании std::experimental::filesystem::path из char* в кодированную строку UTF-8 преобразование не происходит (даже если заголовки используют _To_wide а также _To_byte функционирует внутри. Я написал простой тестовый пример:

#include <string>
#include <experimental\filesystem>

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

static inline std::string FromUtf16(const wchar_t* pUtf16String)
{
    int nUtf16StringLength = static_cast<int>(wcslen(pUtf16String));
    int nUtf8StringLength = ::WideCharToMultiByte(CP_UTF8, 0, pUtf16String, nUtf16StringLength, NULL, 0, NULL, NULL);
    std::string sUtf8String(nUtf8StringLength, '\0');
    nUtf8StringLength = ::WideCharToMultiByte(CP_UTF8, 0, pUtf16String, nUtf16StringLength, const_cast<char *>(sUtf8String.c_str()), nUtf8StringLength, NULL, NULL);
    return sUtf8String;
}

static inline std::string FromUtf16(const std::wstring& sUtf16String)
{
    return FromUtf16(sUtf16String.c_str());
}

static inline std::wstring ToUtf16(const char* pUtf8String)
{
    int nUtf8StringLength = static_cast<int>(strlen(pUtf8String));
    int nUtf16StringLength = ::MultiByteToWideChar(CP_UTF8, 0, pUtf8String, nUtf8StringLength, NULL, NULL);
    std::wstring sUtf16String(nUtf16StringLength, '\0');
    nUtf16StringLength = ::MultiByteToWideChar(CP_UTF8, 0, pUtf8String, nUtf8StringLength, const_cast<wchar_t*>(sUtf16String.c_str()), nUtf16StringLength);
    return sUtf16String;
}

static inline std::wstring ToUtf16(const std::string& sUtf8String)
{
    return ToUtf16(sUtf8String.c_str());
}

int main(int argc, char** argv)
{
    std::string sTest(u8"Kaķis");
    std::wstring sWideTest(ToUtf16(sTest));
    wchar_t pWideTest[1024] = {};
    char pByteTest[1024];
    std::experimental::filesystem::path Path1(sTest), Path2(sWideTest);
    std::experimental::filesystem::v1::_To_wide(sTest.c_str(), pWideTest);
    bool bWideEqual = sWideTest == pWideTest;
    std::experimental::filesystem::v1::_To_byte(pWideTest, pByteTest);
    bool bUtf8Equal = sTest == pByteTest;
    bool bPathsEqual = Path1 == Path2;
    printf("wide equal: %d, utf-8 equal: %d, paths equal: %d\n", bWideEqual, bUtf8Equal, bPathsEqual);
}

Но, как я уже говорил ранее, я просто слепо предполагал, что UTF-8 будет работать. Глядя на std:: экспериментальный::filesystem::path на cppreference.com в разделе конструктора, он на самом деле утверждает, что:

  • Если исходный символьный тип - char, предполагается, что кодировкой источника является собственная узкая кодировка (поэтому в системах POSIX преобразование не выполняется)
  • Если исходный символьный тип - char16_t, используется преобразование из UTF-16 в кодировку собственной файловой системы.
  • Если исходный символьный тип - char32_t, используется преобразование из UTF-32 в кодировку собственной файловой системы.
  • Если исходный символьный тип - wchar_t, предполагается, что входные данные являются кодировкой родного формата (поэтому в Windows преобразование не происходит)

Я не уверен, как интерпретировать первую строку. Во-первых, в нем говорится только о системах POSIX (хотя я не понимаю, что такое родная узкая кодировка, означает ли это, что UTF-8 не будет работать и в POSIX?). Во-вторых, в нем ничего не говорится о Windows, и MSDN тоже об этом молчит. Итак, как свойство обрабатывать инициализацию std::experimental::filesystem::path из символов Unicode в кроссплатформенной безопасной манере?

1 ответ

Решение

"Узкая" (8-битная) кодировка filesystem::path зависит от среды и хоста ОС. Это может быть UTF-8 во многих системах POSIX, но это может и не быть. Если вы хотите использовать UTF-8, вы должны использовать его явно, через std::filesystem::path::u8string() а также std::filesystem::u8path()

Другие вопросы по тегам