"C.UTF-8" Windows 上的 C++ 语言环境?
"C.UTF-8" C++ locale on Windows?
我正在修复一个大型开源跨平台应用程序,以便它可以处理 Windows.
上包含非 ANSI 字符的文件路径
更新:
根据我目前得到的答案和评论(谢谢!)我觉得我应该澄清一些要点:
我不能修改几十个第三方库的代码才能使用std::wchar_t
。这不是一个选择。该解决方案必须使用普通的 ol' std::fopen()
、std::ifstream
等
我在下面概述的解决方案工作在 99%,至少在我正在开发的系统上(Windows 10 版本 1909,构建 18363.535)。我还没有在任何其他系统上测试过。
唯一剩下的问题,至少在我的系统上,基本上是数字格式,我希望替换 std::numpunct
facet 可以解决问题(但我还没有成功)。
我目前的解决方案包括:
将 Windows 上的 LC_CTYPE
类别的 C 语言环境设置为 .UTF-8
(所有其他类别均根据需要设置为 C
语言环境通过应用程序):
// Required by the application.
std::setlocale(LC_ALL, "C");
// On Windows, we want std::fopen() and other functions dealing with strings
// and file paths to accept narrow-character strings encoded in UTF-8.
#ifdef _WIN32
{
#ifndef NDEBUG
char* new_ctype_locale =
#endif
std::setlocale(LC_CTYPE, ".UTF-8");
assert(new_ctype_locale != nullptr);
}
#endif
配置 boost::filesystem::path
以使用 en_US.UTF-8
语言环境,这样它也可以处理包含非 ANSI 字符的路径:
boost::filesystem::path::imbue(std::locale("en_US.UTF-8"));
最后遗漏的一点是使用 C++ 流修复文件 I/O,例如
std::ifstream istream(filename);
最简单的解决方案可能是在应用程序开始时设置全局 C++ 语言环境:
std::locale::global(std::locale("en_US.UTF-8"));
然而,这会弄乱数字的格式,例如1234.56 被格式化为 1,234.56.
是否有语言环境 只是 将编码指定为 UTF-8 而不会混淆数字格式(或其他内容)?
基本上我正在寻找 C.UTF-8
语言环境,但在 Windows 上似乎不存在。
更新:我认为一种解决方案是重置区域设置的某些(大部分?全部?)方面,但我很难找到有关如何执行此操作的信息。
别管语言环境。
在 Windows 上,您应该使用 Microsoft's extension 添加构造函数,将 const std::wchar_t*
(预期指向 UTF-16)添加到 std::ifstream
。
希望您所有的字符串都是 UTF-8,或者一些一致且合理的编码。
所以只需获取 UTF-8 → UTF-16 转换器(它们是轻量级的)并将文件名作为 UTF-16 传递给 std::ifstream
(在 std::wchar_t*
中)。
(一定要 #ifdef
出来,这样就不会在任何其他平台上尝试。)
出于同样的原因,您也应该以同样的方式使用 _wfopen
而不是 std::fopen
。
就是这样。
Windows API 不尊重 CRT 语言环境,fopen
等的 CRT 实现直接调用 narrow-char API,因此更改语言环境不会影响编码。
但是,Windows 2019 年 5 月 10 日更新(版本 1903)introduced a support for UTF-8 in its narrow-char APIs。它可以通过将适当的清单嵌入到您的可执行文件中来启用。不幸的是,这是最近才添加的,因此如果您需要针对较旧的系统,则可能不是一个选项。
您的其他选择包括手动转换为 wchar_t
或使用为您转换的层(例如 Boost.Filesystem,甚至更好,Boost.Nowide)。
我正在修复一个大型开源跨平台应用程序,以便它可以处理 Windows.
上包含非 ANSI 字符的文件路径更新:
根据我目前得到的答案和评论(谢谢!)我觉得我应该澄清一些要点:
我不能修改几十个第三方库的代码才能使用
std::wchar_t
。这不是一个选择。该解决方案必须使用普通的 ol'std::fopen()
、std::ifstream
等我在下面概述的解决方案工作在 99%,至少在我正在开发的系统上(Windows 10 版本 1909,构建 18363.535)。我还没有在任何其他系统上测试过。
唯一剩下的问题,至少在我的系统上,基本上是数字格式,我希望替换
std::numpunct
facet 可以解决问题(但我还没有成功)。
我目前的解决方案包括:
将 Windows 上的
LC_CTYPE
类别的 C 语言环境设置为.UTF-8
(所有其他类别均根据需要设置为C
语言环境通过应用程序):// Required by the application. std::setlocale(LC_ALL, "C"); // On Windows, we want std::fopen() and other functions dealing with strings // and file paths to accept narrow-character strings encoded in UTF-8. #ifdef _WIN32 { #ifndef NDEBUG char* new_ctype_locale = #endif std::setlocale(LC_CTYPE, ".UTF-8"); assert(new_ctype_locale != nullptr); } #endif
配置
boost::filesystem::path
以使用en_US.UTF-8
语言环境,这样它也可以处理包含非 ANSI 字符的路径:boost::filesystem::path::imbue(std::locale("en_US.UTF-8"));
最后遗漏的一点是使用 C++ 流修复文件 I/O,例如
std::ifstream istream(filename);
最简单的解决方案可能是在应用程序开始时设置全局 C++ 语言环境:
std::locale::global(std::locale("en_US.UTF-8"));
然而,这会弄乱数字的格式,例如1234.56 被格式化为 1,234.56.
是否有语言环境 只是 将编码指定为 UTF-8 而不会混淆数字格式(或其他内容)?
基本上我正在寻找 C.UTF-8
语言环境,但在 Windows 上似乎不存在。
更新:我认为一种解决方案是重置区域设置的某些(大部分?全部?)方面,但我很难找到有关如何执行此操作的信息。
别管语言环境。
在 Windows 上,您应该使用 Microsoft's extension 添加构造函数,将 const std::wchar_t*
(预期指向 UTF-16)添加到 std::ifstream
。
希望您所有的字符串都是 UTF-8,或者一些一致且合理的编码。
所以只需获取 UTF-8 → UTF-16 转换器(它们是轻量级的)并将文件名作为 UTF-16 传递给 std::ifstream
(在 std::wchar_t*
中)。
(一定要 #ifdef
出来,这样就不会在任何其他平台上尝试。)
出于同样的原因,您也应该以同样的方式使用 _wfopen
而不是 std::fopen
。
就是这样。
Windows API 不尊重 CRT 语言环境,fopen
等的 CRT 实现直接调用 narrow-char API,因此更改语言环境不会影响编码。
但是,Windows 2019 年 5 月 10 日更新(版本 1903)introduced a support for UTF-8 in its narrow-char APIs。它可以通过将适当的清单嵌入到您的可执行文件中来启用。不幸的是,这是最近才添加的,因此如果您需要针对较旧的系统,则可能不是一个选项。
您的其他选择包括手动转换为 wchar_t
或使用为您转换的层(例如 Boost.Filesystem,甚至更好,Boost.Nowide)。