检查 C 中是不是存在文件的最佳方法是啥?
Posted
技术标签:
【中文标题】检查 C 中是不是存在文件的最佳方法是啥?【英文标题】:What's the best way to check if a file exists in C?检查 C 中是否存在文件的最佳方法是什么? 【发布时间】:2010-09-18 19:22:04 【问题描述】:有没有比简单地打开文件更好的方法?
int exists(const char *fname)
FILE *file;
if ((file = fopen(fname, "r")))
fclose(file);
return 1;
return 0;
【问题讨论】:
您真的只是想检查是否存在?或者您是否要检查并写入该文件(如果该文件尚不存在)。如果是这样,请参阅下面的答案,以获得不受竞争条件影响的版本。 我看不到 - 那种 fopen/fclose 方式有什么问题? @JohannesSchaub-litb:fopen()
/fclose()
方法的一个问题是即使文件存在,您也可能无法打开文件进行读取。比如/dev/kmem
是存在的,但是大部分进程连读都打不开。 /etc/shadow
是另一个这样的文件。当然,stat()
和access()
都依赖于能够访问包含文件的目录;如果你不能这样做(对包含文件的目录没有执行权限),那么所有的赌注都会被取消。
if (file = fopen(fname, "r"))
会发出警告。在 if 语句内使用括号括起来 if ((file = fopen(fname, "r")))
@Joakim (())
正在解决症状,而不是问题。只需将其分成几行;一条额外的线不会有那么大的伤害。 file = fopen(fname, "r");
if (file)
【参考方案1】:
查找access()
函数,在unistd.h
中找到。你可以用
if( access( fname, F_OK ) == 0 )
// file exists
else
// file doesn't exist
您还可以使用R_OK
、W_OK
和X_OK
代替F_OK
来检查读取权限、写入权限和执行权限(分别)而不是存在,您可以 OR 中的任何一个它们在一起(即使用R_OK|W_OK
检查读取和写入权限)
更新:请注意,在 Windows 上,您不能使用 W_OK
可靠地测试写入权限,因为访问功能不考虑 DACL。 access( fname, W_OK )
可能会返回 0(成功),因为文件没有设置只读属性,但您可能仍然没有写入文件的权限。
【讨论】:
让我挑剔:) access() 不是标准函数。如果从广义上理解“跨平台”,这可能会失败:) POSIX 是一个 ISO 标准;它定义了访问()。 C 是另一个 ISO 标准;它没有。 与 access() 相关的陷阱。在使用 access() 和您之后执行的任何其他操作之间存在一个 TOCTOU(检查时间,使用时间)漏洞窗口。 [...待续...] [...continuing...] 更深奥的是,在 POSIX 系统上,access() 检查是否是真实的 UID 和真实的 GID,而不是有效的 UID 和有效的 GID。这仅对 setuid 或 setgid 程序很重要,但它非常重要,因为它可能会给出“错误”的答案。 大多数时候,是的(可以使用access()
来检查文件是否存在),但在SUID 或SGID 程序中,即使这样也可能不正确。如果测试文件位于真实 UID 或真实 GID 无法访问的目录中,access()
可能会在该文件确实存在时报告无此类文件。深奥和不可能?是的。【参考方案2】:
像这样使用stat
:
#include <sys/stat.h> // stat
#include <stdbool.h> // bool type
bool file_exists (char *filename)
struct stat buffer;
return (stat (filename, &buffer) == 0);
然后这样称呼它:
#include <stdio.h> // printf
int main(int ac, char **av)
if (ac != 2)
return 1;
if (file_exists(av[1]))
printf("%s exists\n", av[1]);
else
printf("%s does not exist\n", av[1]);
return 0;
【讨论】:
@LudvigANorin:在这样的系统上,access()
也有问题,并且有一些选项可用于使 access()
和 stat()
处理大文件(大于 2 GB )。
你们中的任何一个都可以指出有关 2 GB 后失败的文档吗?另外,在这种情况下有什么替代方案?
@JonathanLeffler stat
不会遭受与access
相同的 TOCTOU 漏洞吗? (我不清楚这会更好。)
stat()
和 access()
都存在 TOCTOU 漏洞(lstat()
也是如此,但fstat()
是安全的)。这取决于您要根据文件的存在与否执行什么操作。对open()
使用正确的选项通常是处理问题的最佳方法,但制定正确的选项可能会很棘手。另请参阅有关 EAFP(比许可更容易请求宽恕)和 LBYL(在您跳跃之前查看)的讨论——例如,请参阅 LBYL vs EAFP in Java。
@Telemachus 如果你需要避免TOCTOU,至少在Linux系统上你可以open()
TOC 中的文件(open()
结果然后成为文件存在的检查) ,然后在 TOU 中使用此描述符。这样即使文件在 TOU 中不再存在,您仍然可以通过文件描述符访问它。只要有打开它的进程,它的内容就会被保留。【参考方案3】:
通常当你想检查一个文件是否存在时,这是因为如果它不存在你想创建那个文件。 Graeme Perrow's answer 如果您不想想要创建该文件,这很好,但如果您这样做,它很容易受到竞争条件的影响:另一个进程可能会在您检查文件是否存在之间创建该文件,然后您实际上打开它来写它。 (别笑……如果创建的文件是符号链接,这可能会产生不好的安全隐患!)
如果您想检查是否存在并且如果文件不存在则创建文件,原子地以便没有竞争条件,然后使用这个:
#include <fcntl.h>
#include <errno.h>
fd = open(pathname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
if (fd < 0)
/* failure */
if (errno == EEXIST)
/* the file already existed */
...
else
/* now you can use the file */
【讨论】:
如果您要使用 O_CREAT,您需要提供模式(权限)作为 open() 的第三个参数。还要考虑是否应该使用 O_TRUNC 或 O_EXCL 或 O_APPEND。 Jonathan Leffler 是对的,这个例子需要 O_EXCL 才能像写的那样工作。 另外,您需要将模式指定为第三个参数:open(lock, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR) 应该注意的是,这只有在文件系统符合 POSIX 时才安全;特别是,旧版本的 NFS 具有 O_EXCL 应该避免的竞争条件!有一个解决方法,记录在open(2)
中(在 Linux 上;您的操作系统的手册页可能会有所不同),但它相当难看,并且可能无法抵抗恶意攻击者。
请注意,要将其与FILE*
一起使用,您需要使用posix方法fdopen(fd,"flags")
生成FILE*
【参考方案4】:
是的。使用stat()
。请参阅stat(2)
的手册页。
stat()
如果文件不存在将失败,否则很可能成功。如果它确实存在,但您对它所在的目录没有读取权限,它也会失败,但在这种情况下,任何方法都会失败(如何根据访问权限检查您可能看不到的目录的内容?简单地说,你不能)。
哦,就像别人提到的,你也可以使用access()
。不过我更喜欢stat()
,好像文件存在一样,它会立即为我提供很多有用的信息(上次更新时间、文件大小、拥有文件的所有者和/或组、访问权限等) .
【讨论】:
如果您只需要知道文件是否存在,则优先访问。如果您不需要所有额外信息,Stat() 可能会被大量偷听。 实际上,当我使用 ls-command 列出一个目录时,它会为那里存在的每个文件调用 stat ,并且运行 ls 有很大的开销对我来说是相当新的。实际上,您可以在包含数千个文件的目录上运行 ls,它会在几分之一秒内返回。 @Mecki:与支持硬链接的系统上的访问相比,stat 具有非零的额外开销。这是因为 access 只需要查看目录条目,而 stat 也必须查找 inode。在寻道时间不佳的存储设备(例如磁带)上,差异可能很大,因为目录条目和 inode 不太可能彼此相邻。 @Kevin 除非您只将 F_OK 传递给它,否则access()
检查文件的文件访问权限,这些权限存储在该文件的 inode 中,而不是在其目录条目中(至少对于所有具有类似 inode 结构的文件系统)。所以access()
必须以与stat()
必须访问它的方式完全相同的方式访问inode。 所以你所说的只有在你不检查任何权限的情况下才成立! 实际上在某些系统上access()
甚至是在stat()
之上实现的(例如 GNU Hurd 上的 glibc 就是这样做的方式),所以首先没有保证。
@Mecki:谁说过关于检查权限的任何事情?我专门谈论 F_OK。是的,有些系统执行不力。在每种情况下,访问速度至少与 stat 一样快,并且有时可能会更快。【参考方案5】:
FILE *file;
if((file = fopen("sample.txt","r"))!=NULL)
// file exists
fclose(file);
else
//File not found, no memory leak since 'file' == NULL
//fclose(file) would cause an error
【讨论】:
fopen()
是标准 C,它不会去任何地方。它只是被微软“弃用”。不要使用fopen_s()
,除非你想要特定于平台的、不可移植的代码。
这是最兼容所有新旧系统的。
布尔集在哪里?【参考方案6】:
你可以使用 realpath() 函数。
resolved_file = realpath(file_path, NULL);
if (!resolved_keyfile)
/*File dosn't exists*/
perror(keyfile);
return -1;
【讨论】:
【参考方案7】:我认为unistd.h
中的access()函数是Linux
的一个不错的选择(你也可以使用stat)。
你可以这样使用它:
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
void fileCheck(const char *fileName);
int main (void)
char *fileName = "/etc/sudoers";
fileCheck(fileName);
return 0;
void fileCheck(const char *fileName)
if(!access(fileName, F_OK ))
printf("The File %s\t was Found\n",fileName);
else
printf("The File %s\t not Found\n",fileName);
if(!access(fileName, R_OK ))
printf("The File %s\t can be read\n",fileName);
else
printf("The File %s\t cannot be read\n",fileName);
if(!access( fileName, W_OK ))
printf("The File %s\t it can be Edited\n",fileName);
else
printf("The File %s\t it cannot be Edited\n",fileName);
if(!access( fileName, X_OK ))
printf("The File %s\t is an Executable\n",fileName);
else
printf("The File %s\t is not an Executable\n",fileName);
你会得到以下输出:
The File /etc/sudoers was Found
The File /etc/sudoers cannot be read
The File /etc/sudoers it cannot be Edited
The File /etc/sudoers is not an Executable
【讨论】:
【参考方案8】:从 Visual C++ 帮助中,我倾向于使用
/* ACCESS.C: This example uses _access to check the
* file named "ACCESS.C" to see if it exists and if
* writing is allowed.
*/
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
void main( void )
/* Check for existence */
if( (_access( "ACCESS.C", 0 )) != -1 )
printf( "File ACCESS.C exists\n" );
/* Check for write permission */
if( (_access( "ACCESS.C", 2 )) != -1 )
printf( "File ACCESS.C has write permission\n" );
还值得注意的是_access(const char *path,
int mode
)
的模式值:
00:仅存在
02:写权限
04:读取权限
06:读写权限
因为您的fopen
在文件存在但无法按请求打开的情况下可能会失败。
编辑:只需阅读 Mecki 的帖子。 stat()
看起来确实是一种更简洁的方式。哼哼。
【讨论】:
如果您只需要知道文件是否存在,则优先访问。 Stat() 可以有很大的偷听。 永远不要使用 void main。使用 int main。 @Anic17 为什么我们不应该使用void main
?以上是关于检查 C 中是不是存在文件的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
Boto:检查 CloudFormation 堆栈是不是存在的最佳方法是啥?
检查文档是不是存在并根据 CosmosDB 中的结果插入的最佳方法是啥?