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

Cross-platform way to handle std::string/std::wstring with std::filesystem::path

我有一段 C++ 代码示例在 Linux:

上抛出异常
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

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

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

  1. 为什么这个宽字符串 (wchar_t) 是 "invalid or incomplete multibyte or wide character"?
  2. 展望未来,我如何使相关代码跨平台到 Windows and/or Linux 上的 运行。
    • 我需要使用辅助函数吗?
    • 我需要从程序员的 PoV 中强制执行哪些规则?
    • 我在这里看到 response 说 "Don't use wide strings on Linux",我对 Windows 使用相同的规则吗?

Linux环境(别忘了我想运行跨平台):

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

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

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

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

#ifdef _WINDOWS_PLATFORM
    //windows I/O setup
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
#endif

fs::path path = fs::u8path(u8"ελληνικά.txt");

#ifdef _WINDOWS_PLATFORM
    std::wcout << "UTF16: " << path << std::endl;
#else
    std::cout <<  "UTF8:  " << path << std::endl;
#endif

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

定义了 UNICODE
#ifdef _WINDOWS_PLATFORM
#define _TEXT(quote) L##quote
#define _tcout std::wcout
#else
#define _TEXT(quote) u8##quote
#define _tcout std::cout
#endif

fs::path path(_TEXT("ελληνικά.txt"));
_tcout << path << std::endl;

另见
https://en.cppreference.com/w/cpp/filesystem/path/native


注意,Visual Studio 有一个用于 std::fstream 的特殊构造函数,它允许使用 UTF16 文件名,并且它与 UTF8 read/write 兼容。例如,以下代码将在 Visual Studio:

中工作
fs::path utf16 = fs::u8path(u8"UTF8 filename ελληνικά.txt");
std::ofstream fout(utf16);
fout << u8"UTF8 content ελληνικά";

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

看起来像 a GCC bug

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

Clang 显示正确的行为。