我们想写入给定目录中的“foo.txt”。如果“foo.txt”已经存在,我们要写入“foo-1.txt”,以此类推。
有一些代码片段试图回答这个问题,但没有一个是非常令人满意的。例如,CocoaDev 的这个解决方案使用 NSFileManager 来测试路径是否存在以创建安全路径。但是,这会导致获取路径和写入路径之间出现明显的竞争条件。尝试原子写入会更安全,并在失败时循环数字后缀。
去吧!
我们想写入给定目录中的“foo.txt”。如果“foo.txt”已经存在,我们要写入“foo-1.txt”,以此类推。
有一些代码片段试图回答这个问题,但没有一个是非常令人满意的。例如,CocoaDev 的这个解决方案使用 NSFileManager 来测试路径是否存在以创建安全路径。但是,这会导致获取路径和写入路径之间出现明显的竞争条件。尝试原子写入会更安全,并在失败时循环数字后缀。
去吧!
使用带有和选项的open
系统调用。如果文件不存在,将创建它,打开它,并将文件描述符返回给您;如果确实存在,将失败并设置为.O_EXCL
O_CREAT
open
open
errno
EEXIST
从那里,应该很明显如何构造循环尝试递增文件名,直到它返回文件描述符或构造太长的文件名。在后一点上,请确保您检查errno
失败的open
时间——<code>EEXIST,ENAMETOOLONG
这只是您可能遇到的两个错误。
int fd;
uint32_t counter;
char filename[1024]; // obviously unsafe
sprintf(filename, "foo.txt");
if( (fd = open(filename, O_CREAT | O_EXCL | O_EXLOCK, 0644)) == -1 && errno == EEXIST )
{
for( counter = 1; counter < UINT32_MAX; counter++ ) {
sprintf(filename, "foo-%u.txt", counter);
if( (fd = open(filename, O_CREAT | O_EXCL | O_EXLOCK, 0644)) == -1 && errno == EEXIST )
continue;
else
break;
}
}
if( fd == -1 && counter == UINT32_MAX ) {
fprintf(stderr, "too many foo-files\n");
} else if( fd == -1 ) {
fprintf(stderr, "could not open file: %s\n", strerror(errno));
}
// otherwise fd is an open file with an atomically unique name and an
// exclusive lock.
怎么样:
您将基本上重新创建 Cocoa 的原子文件写入处理,但添加了确保唯一文件名的功能。这种方法的一大优点是,如果断电或您的应用程序在写入过程中崩溃,半成品文件将被隐藏在 tmp 文件夹中并被系统删除;没有留给用户尝试和使用。