std::filesystem::path::u8string 可能 return 不是有效的 UTF-8?

std::filesystem::path::u8string might not return valid UTF-8?

考虑此代码,运行 在 Linux 系统 (Compiler Explorer link) 上:

#include <filesystem>
#include <cstdio>

int main()
{
    try
    {
        const char8_t bad_path[] = {0xf0, u8'a', 0};  // invalid utf-8, 0xf0 expects continuation bytes
        std::filesystem::path p(bad_path);

        for (auto c : p.u8string())
        {
            printf("%X ", static_cast<uint8_t>(c));
        }
    }
    catch (const std::exception& e)
    {
        printf("error: %s\n", e.what());
    }
}

它故意使用不正确的 UTF-8 编码的字符串构造一个 std::filesystem::path 对象(0xf0 开始一个 4 字节字符,但 'a' 不是连续字节;更多信息 here).

调用u8string时,不抛异常;我发现这令人惊讶,因为 documentation at cppreference 指出:

  1. The result encoding in the case of u8string() is always UTF-8.

检查 implementation of LLVM's libcxx,我发现确实没有执行任何验证 - std::filesystem::path 内部保存的字符串只是复制到 u8string 并返回:

_LIBCPP_INLINE_VISIBILITY _VSTD::u8string u8string() const { return _VSTD::u8string(__pn_.begin(), __pn_.end()); }

GCC 实现 (libstdc++) 表现出相同的行为。

当然,这是一个人为的例子,因为我故意从无效字符串构造路径以保持简单。但据我所知,Linux kernel/filesystems 不强制文件路径是有效的 UTF-8 字符串,因此我可能会遇到像“在野外”这样的路径,例如迭代目录。

我的结论 std::filesystem::path::u8string 实际上并不能保证将返回有效的 UTF-8 字符串,尽管文档中有说明,我的结论是否正确?如果是这样,这种设计背后的动机是什么?

当前 C++ 标准在 fs.path.type.cvt 中声明:

char8_­t: The encoding is UTF-8. The method of conversion is unspecified.

还有

If the encoding being converted to has no representation for source characters, the resulting converted characters, if any, are unspecified.

因此,简而言之,任何涉及构成路径的字节的实际解释的内容都是未指定的,这意味着实现可以自由地处理它们认为合适的无效数据。所以是的,std::filesystem::path::u8string() 并不能真正保证返回有效的 UTF-8 字符串。

关于动机:标准对此只字未提。但是通过查看标准所基于的 boost::filesystem 可能会有一个想法。 The documentation 状态:

When a class path function argument type matches the operating system's API argument type for paths, no conversion is performed rather than conversion to a specified encoding such as one of the Unicode encodings. This avoids unintended consequences, etc.

我猜您使用的是 posix 系统,在这种情况下,底层操作系统 API is most likely using UTF-8 or binary filenames。因此,输入保持原样,以免陷入任何转换问题。 另一方面,Windows 使用的是 UTF-16,因此在构建路径时需要转换字符串,当输入是无效的 UTF-8 编码 (godbolt) 时会导致异常。