除非文件存在于C中,否则如何安全地写入文件?
我正在尝试执行以下操作:除非文件存在于C中,否则如何安全地写入文件?,c,file,C,File,我正在尝试执行以下操作: 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"); }
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");
}
我当然可以使用,但正如在答案评论中所说的,这将是一个种族条件(特别是)
安全地创建和写入文件的最简单方法是什么,除非文件已经存在,而不创建竞争条件?在纯C中没有跨平台、可移植的方法来实现这一点,因为C的标准库缺少高级IO操作,例如文件锁定,也没有系统范围的互斥体库。C甚至没有“列表目录”功能 我觉得最好的解决方案是通过提示用户干预并让他们选择如何继续(例如删除文件、强制覆盖等)来处理错误情况
C++11(…最后)中提供了一个功能更全面的IO库,但这意味着放弃pure-C。另一种选择是使用跨平台包装库,包装平台特定的IO函数。如果使用GNU C库,那么调用
fopen(“filename”,“xw+”)
可能会满足您的需求。字符串x
执行以下操作:
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");
}
x
:以独占方式打开文件(如打开(2)的O_EXCL标志)。如果文件已经存在,fopen()将失败,并将errno设置为EEXIST。fdopen()忽略此标志
另一个选项的功能是:
w+
:打开进行读写。如果文件不存在,则创建该文件,否则将截断该文件。流位于文件的开头
您还可以使用:
a
:打开以进行追加(在文件末尾写入)。如果文件不存在,则创建该文件。流位于文件的末尾
a+
:打开以进行读取和追加(在文件末尾写入)。如果文件不存在,则创建该文件。读取的初始文件位置位于文件的开头,但输出始终附加到文件的结尾
还有更多的选择。有关详细信息,请参见您必须将open(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);
}
#包括
#包括
#包括
#包括
#包括
int main(int argc,char*argv[]){
char*program=argv[0];
char*filename=argv[1];
如果(argc!=2){
fprintf(stderr,“%s:应为单个文件参数。\n”,程序);
出口(1);
}
int flags=O|u EXCL | O|u CREAT | O|u WRONLY;
int模式=0666;
int-fd;
如果((fd=open(文件名、标志、模式))=-1){
fprintf(stderr,“%s:无法打开带有标志0x%04X的%s:%s\n”,
程序、文件名、标志、strerror(errno));
出口(2);
}
文件*fp=fdopen(fd,“w”);
如果(fp==NULL){
fprintf(stderr,“%s:无法打开文件描述符%d:%s\n”,
程序,fd,strerror(errno));
出口(3);
}
if(fprintf(fp,“12345\n”)==EOF){
fprintf(stderr,“%s:无法写入%s:%s\n”,
程序,文件名,strerror(errno));
出口(4);
}
如果(fclose(fp)=EOF){
fprintf(stderr,“%s:无法关闭%s:%s\n”,
程序,文件名,strerror(errno));
出口(5);
}
出口(0);
}
这些开放(2)的旗帜是少数
这不能保证在远程文件系统上可靠地工作。特别是,NFS打破了POSIX关于原子创建的规则。[sic]使用带有O_EXCL标志的open()。O_EXCL确保此调用创建文件:如果此标志与O_CREAT一起指定,并且路径名已存在,则open()将失败。@Alon
O_EXCL
不可移植。这是有意义的,但仍然需要两个单独的系统调用(if(fileexists(foo)){prompt…}else{fprintf(foo,bar);}
或类似的内容),这仍然使竞争条件成为可能。如何在不创建竞争条件的情况下做到这一点?@Doorknob不,每个(现代)平台都提供了一个原子文件锁定API,您可以从C中使用它们,但在portable-C中无法实现。好的,您可以提供一个链接或某种资源来演示如何跨平台进行文件锁定吗?谢谢@对不起,我有点太热心了。我已经将我的断言更改为C缺少目录处理函数。C11具有@NZD的答案中提到的x
mode字符。