给定CWD中的文件text.txt
,从fopen()
到“这个文件是否存在?”这个问题,我得到了两个不同的答案。对于a/b/../../test.txt
(减少为test.txt
):
但是,如果我然后mkdir -p a/b
,Linux会更改其音调并说a/b/../../test.txt
现在存在。
好像Linux在处理相对路径时检查每个目录是否存在(如果没有,则检查早期目录),而不是先折叠任何../
的目录,然后执行文件存在检查,就像Windows出现一样要做。
两个问题:
fopen()
在这样的相对路径下表现如何?我可以找到的fopen()
的文档/说明只是表明它接受了相对路径(也许有一些示例),而不是在中间路径../
的情况下应该如何解析这些路径。 测试程序:
#include <filesystem>
#include <fstream>
#include <iostream>
#include <cstdio>
void file_exists( const char* filename )
{
FILE* file = fopen( filename, "r" );
std::cout << ( file ? "Y" : "N" ) << ": " << filename << std::endl;
if( file )
{
fclose( file );
}
}
int main( int, char** )
{
// initial tests
std::filesystem::remove( "test.txt" );
std::filesystem::remove( "doesnt-exist.txt" );
file_exists( "test.txt" );
file_exists( "doesnt-exist.txt" );
std::cout << std::endl;
// create file
std::cout << "creating text.txt..." << std::endl;
std::ofstream output( "test.txt", std::ios::trunc );
output << "test" << std::endl;
output.close();
// more tests
file_exists( "test.txt" );
file_exists( "doesnt-exist.txt" );
file_exists( "a/b/../../test.txt" );
file_exists( "a/b/../../doesnt-exist.txt" );
std::cout << std::endl;
std::cout << "creating directory 'a/b'..." << std::endl;
std::filesystem::create_directories( "a/b" );
file_exists( "a/b/../../test.txt" );
file_exists( "a/b/../../doesnt-exist.txt" );
std::cout << std::endl;
// cleanup
std::filesystem::remove( "test.txt" );
std::filesystem::remove_all( "a/b" );
return 0;
}
结果:
Windows 10,VS2017:
N: test.txt
N: doesnt-exist.txt
creating text.txt...
Y: test.txt
N: doesnt-exist.txt
Y: a/b/../../test.txt # file exists, expected behavior
N: a/b/../../doesnt-exist.txt
creating directory 'a/b'...
Y: a/b/../../test.txt
N: a/b/../../doesnt-exist.txt
Ubuntu 20.04,g++ (Ubuntu 9.3.0-10ubuntu2) 9.3.0
N: test.txt
N: doesnt-exist.txt
creating text.txt...
Y: test.txt
N: doesnt-exist.txt
N: a/b/../../test.txt # file *doesn't* exist, unexpected behavior
N: a/b/../../doesnt-exist.txt
creating directory 'a/b'...
Y: a/b/../../test.txt
N: a/b/../../doesnt-exist.txt
文件名和路径名是特定于实现的。当您意识到C:X
在Linux和Windows中是完全有效的文件名,但含义却大不相同时,这显然很有意义。
在Linux(如UNIX)上,..
是真实目录条目。也就是说,a/b/../../
包含4个实际目录条目:显然是a和b,但是b中的..
和a中的..
。 fopen
不需要做任何特殊的事情:操作系统只检查实际的目录条目。
在Windows上,必须首先将路径a/b/../../
转换为标准格式。也就是说,它应该以\??\
开头。您可能以前没有看过这些路径,但是它们是Windows内部使用的路径。所有这些路径都是绝对路径,并且这些always
a/b/../../
的转换涉及为当前工作的驱动器和目录添加前缀,用\
替换/
并删除..
如您所见,这些是解决..
的完全不同的方法。您的fopen
调用仅遵循本地约定,通常只需将字符串传递给OS。 (但是在Windows上,fopen
可能也预先进行了/\
转换,请确保。)
[如果另一个OS选择^
表示“父目录”,则您希望fopen("a/b/^/^")
也遵循该约定,因此..
不是一个特殊的字符串。在这样的平台上,..
甚至可能是子目录的有效名称。
[std::filesystem::path::lexically_normal可能有助于纯粹按词法标准化路径(不遵循simlink,检查路径是否存在,...)。