让我们考虑以下代码,该代码列出了作为程序第一个参数给出的路径的目录内容:
#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';
}
乍一看,它似乎非常精简,可移植并且符合C ++标准(请忽略,如果目录不存在,它不会捕获异常)。
但是,似乎有一些陷阱。特别是,C ++标准似乎没有强制argv[1]
的编码与std::filesystem::path
构造函数接受的编码匹配,也似乎没有强制std::filesystem::path::string()
返回的编码与std::cout
。
相反,该标准似乎引入了新术语“本机编码”,该术语可能与执行字符集编码不同,并且定义为:
窄字符串的本机编码是 路径名([fs.class.path])的系统相关当前编码。
根据我对标准的理解,如果std::filesystem::path::value_type
匹配char
的{{1}}类型(在任何POSIX系统上都是如此),则会在编码之间发生不转换 )。
例如,这似乎允许遵循的实现,其中执行字符集编码(因此argv[1]
的编码和argv[1]
接受的编码)是EBCDIC,但是字符串的编码文件系统库接受并提供的是ISO 8859-1,在两者之间不执行任何转换,这使得文件系统库实质上无用。 更糟糕的是,尚无法确定两种编码是否相同。
如果您开始编写用于删除文件的实用程序,并且std::cout
提供的要删除的文件与以文件系统库的本机编码解释的完全不同的文件匹配时,这甚至会变得很危险。
请注意,我不关心使用与程序所使用的编码不同的文件系统。我担心的是,该标准似乎并未要求对这些编码进行任何转换。
argv[1]
和u8path()
函数在这里也没有用,因为该标准也没有提供在UTF-8和执行字符集编码(u8string()
和argv[1]
。
是否有任何可移植的,与编码无关和符合标准的方式来做到这一点?
答案 0 :(得分:4)
不,这不只是理论上的。
在Windows系统上,路径为UTF-16,并且path::value_type
为wchar_t
,而不是从char
获得的char** argv
。这本身不是问题-可以从path
创建char*
。但是,并非每个Windows文件名都可以表示为char*
。因此,该程序无法列出某些名称不能表示为char*
的目录的内容。
现在您会认为Linux会更好。实际上并非完全如此-您为文件名获取的字节可能取决于whether you entered them on a keyboard or via TAB completion!