我试图做以下事情:
FILE* f = fopen_unless_exists("example.txt");
if (f != NULL) {
fprintf(f, "foo bar baz\n");
} else {
// f should be NULL if example.txt already exists
fprintf(stderr, "Error: cannot write to file or file already exists");
}
我当然可以使用one of the methods mentioned in a related question,但正如对答案的评论所述,这将是一种竞争条件(具体而言,TOCTOU)。
安全地创建和写入文件的最简单方法是什么,除非文件已存在,否则不会产生竞争条件?
答案 0 :(得分:1)
您必须使用打开(2)系统调用O_EXCL|O_CREAT|O_WRONLY
,然后在该描述符上调用 fdopen (3)。
#include <sys/errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *program = argv[0];
char *filename = argv[1];
if (argc != 2) {
fprintf(stderr, "%s: expected a single file argument.\n", program);
exit(1);
}
int flags = O_EXCL|O_CREAT|O_WRONLY;
int mode = 0666;
int fd;
if ((fd = open(filename, flags, mode)) == -1) {
fprintf(stderr, "%s: cannot open %s with flags 0x%04X: %s\n",
program, filename, flags, strerror(errno));
exit(2);
}
FILE *fp = fdopen(fd, "w");
if (fp == NULL) {
fprintf(stderr, "%s: cannot fdopen file descriptor %d: %s\n",
program, fd, strerror(errno));
exit(3);
}
if (fprintf(fp, "12345\n") == EOF) {
fprintf(stderr, "%s: cannot write to %s: %s\n",
program, filename, strerror(errno));
exit(4);
}
if (fclose(fp) == EOF) {
fprintf(stderr, "%s: cannot close %s: %s\n",
program, filename, strerror(errno));
exit(5);
}
exit(0);
}
那些 open (2)标志属于少数guaranteed by POSIX。
这不能保证在远程文件系统上可靠地工作。特别是,NFS打破了关于原子创建的POSIX规则。 [原文如此]
答案 1 :(得分:0)
在纯C中没有跨平台,可移植的方式来执行此操作,因为C&C的标准库缺少高级IO操作,例如文件锁定,也没有系统范围的互斥库。 C甚至没有&#34;列表目录&#34;功能
我认为最好的解决方案是通过提示用户进行干预并让他们选择如何继续(例如删除文件,强制覆盖等)来处理错误情况。
C ++ 11中存在功能更全面的IO库(......最终),但这意味着放弃了纯C。另一种方法是使用跨平台的包装程序库来包装特定于平台的IO函数。
答案 2 :(得分:0)
如果您使用GNU C库,则调用fopen("filename", "xw+")
可能会执行您想要的操作。字符串x
执行以下操作:
x
:独占打开文件(如open(2)的O_EXCL标志)。如果该文件已存在,则fopen()失败,并将errno设置为EEXIST。 fdopen()忽略此标志。
另一个选项的功能是:
w+
:开放阅读和写作。如果文件不存在,则创建该文件,否则将被截断。流位于文件的开头。
您也可以使用:
a
:打开追加(在文件末尾写)。如果文件不存在,则创建该文件。流位于文件末尾。
a+
:打开阅读和追加(在文件末尾写)。如果文件不存在,则创建该文件。用于读取的初始文件位置位于文件的开头,但输出始终附加到文件的末尾。
还有更多选择。有关详细信息,请参阅fopen