如果以“a+b”模式打开文件,fseek()函数会将文件指针移动到文件开头吗?

13

我想要使用"a+b"模式打开文件,即如果文件不存在,则会自动创建,但如果存在,则不希望覆盖它。我希望能够读写文件。

该文件是二进制的,我想要在其中保存特定struct的记录。因此,我想使用fseek()将光标移到我想要的记录处,然后使用fwrite()保存记录。

代码如下(MyRecord是一个typedef到一个struct,而FILENAME是一个#define到文件名):

int saveRecord(MyRecord *pRecord, int pos)
{
    FILE* file = fopen(FILENAME, "a+b");
    if (file == NULL)
    {
        printf("Unable to open file %s\n", FILENAME);
        return 0;
    }

    fseek(file, pos * sizeof(MyRecord), SEEK_SET);
    fwrite(pRecord, sizeof(MyRecord), 1, file);
    fclose(file);
    return 1;
}
然而,即使我将pos设置为0,此代码仍将记录附加到文件末尾。为什么在追加模式下使用fseek()SEEK_SET不起作用?我知道可以使用"r+b"打开它,如果失败则使用"wb"打开它,但我想知道为什么这样做不行,以及为什么fseek()在使用SEEK_SET时会将文件指针留在结尾。如果有任何关于此行为已被记录的参考资料,我将不胜感激(因为我找不到任何资料,或者我使用了错误的关键字)。
3个回答

20
那是因为在a模式下,向FILE*写入总是追加到末尾。而fseek仅在此模式下设置读取指针。C标准7.19.5.3 fopen中有记录:

以附加模式(即mode参数的第一个字符为'a')打开文件,将导致所有后续写入都强制写入当前文件结尾,而不管中间调用了多少次fseek函数。


4

普通的C语言没有任何可行的方法来达到您想要的效果。如果您使用的是POSIX系统或类似的系统,可以使用fd=open(FILENAME, O_CREAT|O_RDRW, 0666),然后使用fdopen(fd, "rb+")

编辑:您可以尝试另一种纯C语言实现的方法:

f = fopen(FILENAME, "a+b");
if (!f) /* ... */
tmp = freopen(0, "r+b", f);
if (tmp) f = tmp;
else /* ... */

是的,我猜那应该可以。不过我需要这个在纯 ANSI C 上运行。还是给你点赞,因为你提供了一个好的替代方案。 - jbx
你尝试过使用 freopen 的解决方案吗?不确定它在实践中是否有效(freopen 规范不清,理论上可能是无用的),但或许可以尝试一下。 - R.. GitHub STOP HELPING ICE

4

使用“r+b”模式,如果失败则回退到“w+b”模式。

“a+b”模式允许您读取和追加;“r+b”模式允许随机读写。

fopen文档描述了不同模式下文件的行为。


这里存在竞态条件,可能会破坏你的文件。 - R.. GitHub STOP HELPING ICE
@R..:什么是竞态条件?我应该说“如果失败并且errno == ENOENT,则回退到“wb”吗?” - pmg
我假设使用一个多任务操作系统,另一个进程可能会在第一个fopen失败后但在第二个fopen尝试之前创建该文件并将其填充数据。如果OP正在处理嵌入式系统或任何已知不会发生此情况的封闭系统,则可能不是问题。 - R.. GitHub STOP HELPING ICE
我明白了,谢谢@R..。如果这是一个问题,不同进程同时在相同位置使用fwrite也是一个问题;OP可以使用相同的方法来管理这两个问题。 - pmg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接