我很高兴看到std::experimental::filesystem
在 Visual Studio 2017 中增加了对 的支持,但现在遇到了 Unicode 问题。我有点盲目地假设我可以在任何地方使用 UTF-8 字符串,但是失败了 - 当构造std::experimental::filesystem::path
从 achar*
到 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 会起作用。在构造函数部分下查看 cppreference.com上的std::experimental::filesystem::path它实际上指出:
- 如果源字符类型是 char,则假定源的编码是本机窄编码(因此在 POSIX 系统上不会发生转换)
- 如果源字符类型是 char16_t,则使用从 UTF-16 到本机文件系统编码的转换。
- 如果源字符类型是 char32_t,则使用从 UTF-32 到本机文件系统编码的转换。
- 如果源字符类型是 wchar_t,则假定输入是本机宽编码(因此在 Windows 上不进行转换)
我不确定如何解释第一行。首先,它只说明了关于 POSIX 系统的一些内容(即使我不明白什么是原生窄编码,这是否意味着 UTF-8 也不能在 POSIX 上工作?)。其次,它没有说明任何关于 Windows 的内容,MSDN 也对此保持沉默。那么,如何以std::experimental::filesystem::path
跨平台安全的方式对 Unicode 字符的初始化进行属性处理呢?