使用 std::filesystem::path 处理 std::string/std::wstring 的跨平台方法

Zer*_*ect 6 character-encoding std-filesystem

我有一段在 Linux 上引发异常的 C++ 代码示例:

namespace fs = std::filesystem;
const fs::path pathDir(L"/var/media");
const fs::path pathMedia = pathDir / L"COMPACTO - Diogo Poças.mxf" // <-- Exception thrown here
Run Code Online (Sandbox Code Playgroud)

抛出的异常是: filesystem error: Cannot convert character sequence: Invalid in or incomplete multibyte or wide character

我推测这个问题与ç字符的使用有关。

  1. 为什么这个宽字符串 (wchar_t) 是“无效或不完整的多字节或宽字符”?
  2. 展望未来,我如何使相关代码跨平台运行在 Windows 和/或 Linux 上。
    • 是否有我需要使用的辅助函数?
    • 我需要从程序员的 PoV 中强制执行哪些规则?
    • 我在这里看到一个回复说“不要在 Linux 上使用宽字符串”,我是否对 Windows 使用相同的规则?

Linux 环境(不要忘记我想跨平台运行的事实):

  • Ubuntu 18.04.3
  • 海湾合作委员会 9.2.1
  • C++17

Bar*_*ani 5

不幸的std::filesystem是,编写时并没有考虑到操作系统兼容性,至少没有像宣传的那样。

\n\n

对于基于 Unix 的系统,我们需要 UTF8(u8"string"或仅"string"取决于编译器)

\n\n

对于 Windows,我们需要 UTF16 ( L"string")

\n\n

在 C++17 中你可以使用filesystem::u8path(由于某种原因在 C++20 中已弃用)。在 Windows 中,这会将 UTF8 转换为 UTF16。现在您可以将 UTF16 传递给 API。

\n\n
#ifdef _WINDOWS_PLATFORM\n    //windows I/O setup\n    _setmode(_fileno(stdin), _O_WTEXT);\n    _setmode(_fileno(stdout), _O_WTEXT);\n#endif\n\nfs::path path = fs::u8path(u8"\xce\xb5\xce\xbb\xce\xbb\xce\xb7\xce\xbd\xce\xb9\xce\xba\xce\xac.txt");\n\n#ifdef _WINDOWS_PLATFORM\n    std::wcout << "UTF16: " << path << std::endl;\n#else\n    std::cout <<  "UTF8:  " << path << std::endl;\n#endif\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者使用您自己的宏为 Windows 设置 UTF16 ( L"string"),为基于 Unix 的系统设置 UTF8 (u8"string"或只是"string")。确保UNICODE是为 Windows 定义的。

\n\n
#ifdef _WINDOWS_PLATFORM\n#define _TEXT(quote) L##quote\n#define _tcout std::wcout\n#else\n#define _TEXT(quote) u8##quote\n#define _tcout std::cout\n#endif\n\nfs::path path(_TEXT("\xce\xb5\xce\xbb\xce\xbb\xce\xb7\xce\xbd\xce\xb9\xce\xba\xce\xac.txt"));\n_tcout << path << std::endl;\n
Run Code Online (Sandbox Code Playgroud)\n\n

另请参阅
\n https://en.cppreference.com/w/cpp/filesystem/path/native

\n\n


\n注意,Visual Studio 有一个特殊的构造函数std::fstream,允许使用 UTF16 文件名,并且它兼容 UTF8 读/写。例如,以下代码将在 Visual Studio 中运行:

\n\n

fs::path utf16 = fs::u8path(u8"UTF8 filename \xce\xb5\xce\xbb\xce\xbb\xce\xb7\xce\xbd\xce\xb9\xce\xba\xce\xac.txt");\nstd::ofstream fout(utf16);\nfout << u8"UTF8 content \xce\xb5\xce\xbb\xce\xbb\xce\xb7\xce\xbd\xce\xb9\xce\xba\xce\xac";\n
Run Code Online (Sandbox Code Playgroud)\n\n

我不确定 Windows 上运行的最新 gcc 版本是否支持这一点。

\n

  • `u8path(...)` 已被弃用,因为在 C++20 中,我们有不同的 u8string 和 char8_t*,这意味着 UTF-8(而不是没有指定编码的纯 string/char*)。`std::filesystem::path` 可以接受它们作为构造函数参数,从而使 `u8path` 变得多余。 (3认同)

ple*_*ndo 5

看起来像一个海湾合作委员会错误

根据std::filesystem::path::path您应该能够使用宽字符串调用 std::filesystem::path 构造函数,并且独立于底层平台(这就是 std::filesystem 的全部要点)。

Clang 显示了正确的行为。