std::filesystem::path::u8string 可能不会返回有效的 UTF-8?

use*_*520 5 c++ character-encoding c++20

考虑在 Linux 系统上运行的这段代码(编译器资源管理器链接):

#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());
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

  1. u8string() 的结果编码始终为 UTF-8。

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

_LIBCPP_INLINE_VISIBILITY _VSTD::u8string u8string() const { return _VSTD::u8string(__pn_.begin(), __pn_.end()); }
Run Code Online (Sandbox Code Playgroud)

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

当然,这是一个人为的示例,因为我故意从无效字符串构建路径以使事情简单。但据我所知,Linux 内核/文件系统并不强制文件路径是有效的 UTF-8 字符串,因此我可能会在迭代目录时遇到类似“野外”的路径。

我是否可以得出这样的结论std::filesystem::path::u8string:实际上并不能保证返回有效的 UTF-8 字符串,无论文档如何规定?如果是这样,这个设计背后的动机是什么?

Gug*_*ugi 4

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

\n
\n

char8_\xc2\xadt:编码为UTF-8。\n转换方法未指定。

\n
\n

并且

\n
\n

如果要转换的编码没有源字符的表示形式,则转换后的字符(如果有)是未指定的。

\n
\n

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

\n

关于动机:标准没有提及。boost::filesystem但通过查看该标准的基础,人们可能会有所了解。文档指出:

\n
\n

当类路径函数参数类型与操作系统的路径 API 参数类型匹配时,不会执行任何转换,而是转换为指定的编码(例如 Unicode 编码之一)。这可以避免意外的后果等。

\n
\n

我猜您使用的是 posix 系统,在这种情况下,底层操作系统 API很可能使用 UTF-8 或二进制文件名。因此,输入保持原样,以免遇到任何转换问题。\n另一方面,Windows 使用 UTF-16,因此需要在构造路径时转换字符串,从而在输入为无效的 UTF-8 编码 ( godbolt )。

\n