Con*_*ter 6 c++ filesystems character-encoding c++-standard-library c++17
让我们考虑以下代码,列出作为程序的第一个参数给出的路径的目录内容:
#include <filesystem>
#include <iostream>
int main(int argc, char **argv)
{
if(argc != 2)
std::cerr << "Please specify a directory.\n";
for(auto& p: std::filesystem::directory_iterator(argv[1]))
std::cout << p << '\n';
}
Run Code Online (Sandbox Code Playgroud)
乍一看,这似乎非常精简,可移植并符合C++标准(如果目录不存在,请忽略它不会捕获异常).
然而,似乎存在一些陷阱.特别是,C++标准似乎并没有强制要求构造函数argv[1]接受的匹配编码,std::filesystem::path也没有强制要求由std::filesystem::path::string()接受的匹配返回的编码std::cout.
恰恰相反,该标准似乎引入了新术语"本机编码",它可能与执行字符集编码不同,定义如下:
窄字符串的本机编码是与操作系统相关的路径名的当前编码([fs.class.path]).
根据我对标准的阅读,如果匹配类型(在任何POSIX系统上都是如此),则编码之间不会发生转换.std::filesystem::path::value_typecharargv[1]
例如,这似乎允许一致的实现,其中执行字符集编码(因此编码argv[1]和接受的编码std::cout)是EBCDIC,但文件系统库接受和提供的字符串的编码是ISO 8859-1,两者之间没有进行转换,使得文件系统库基本没用.更糟糕的是,没有办法弄清楚这两种编码是否相同.
如果您开始编写删除文件的实用程序,并且在文件argv[1]系统库的本机编码中解释时匹配完全不同的文件,那么这甚至会变得危险.
请注意,我不关心使用与程序使用的编码不同的文件系统.我担心的是,该标准似乎并未要求对这些编码进行任何转换.
这里的函数u8path()和u8string()函数都没用,因为标准也没有办法在UTF-8和执行字符集编码(由argv[1]和使用std::cout)之间进行转换.
是否有任何便携式,编码不可知和标准兼容的方法来做到这一点?
不,这不仅仅是理论上的。
在 Windows 系统上,路径为 UTF-16,并且path::value_type是wchar_t,而不是char您从 获得的路径char** argv。这本身不是问题 -path可以从char*. 但是,并非每个 Windows 文件名都可以表示为char*. 因此,程序无法列出某些名称不能表示为 的目录的内容char*。
现在你可能会认为 Linux 会更好。实际上情况并非完全如此 - 您获得的文件名字节可能取决于您是通过键盘输入还是通过 TAB 补全输入它们!