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 指出:
- 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) 时会导致异常。
考虑此代码,运行 在 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 指出:
- 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) 时会导致异常。