6

我有一段在 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

抛出的异常是: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
  • GCC 9.2.1
  • C++17
4

2 回答 2

4

看起来像一个 GCC 错误

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

Clang 显示正确的行为。

于 2020-01-12T21:22:55.297 回答
3

不幸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 的系统 (u8"string"或只是"string") 设置 UTF8。确保UNICODE为 Windows 定义。

#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 读/写。例如,以下代码将在 Visual Studio 中工作:

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

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

于 2019-10-24T16:49:43.557 回答