在 C 语言中使用 fseek 随机访问文件

Random access of a file with fseek in C

提问人:Fraser Orr 提问时间:11/8/2023 更新时间:11/8/2023 访问量:96

问:

我正在尝试在现有文件的中间写入一些数据,但是,目前尚不清楚使用哪种模式打开。如果我用“w”打开,它会截断文件,所以“a”似乎是显而易见的选择。我想做的是用“a”打开,然后寻求必要的位置。但是,当我这样做时,它总是写到最后:

FILE* t = fopen("ttt", "w");
fprintf(t, "1234567890");
fclose(t);
t = fopen("ttt", "a");
int result = fseek(t, 2, SEEK_SET);
fprintf(stderr, "%d", result);
fprintf(t, "xxx");
fclose(t);

我希望测试文件 ttt 包含“12xxx67890”,但它包含“1234567890xxx”。(fseek 的结果为 0。

关于如何实现这一点的任何建议?

C 文件系统

评论

1赞 Joel 11/8/2023
cppreference - fopen, fopen_s在追加文件访问模式下,无论文件位置指示器的当前位置如何,数据都会写入文件末尾。
1赞 Fraser Orr 11/8/2023
这是否意味着它不能用 FILE 完成,我必须用文件描述符进行基本打开?
3赞 Joel 11/8/2023
我对 C 文件 IO 不太熟悉,但它应该适用于 .注意:在更新模式('+')下,可以同时执行输入和输出,但如果没有对fflush、fseek、fsetpos或rewind的干预调用,输出不能跟后跟着输入,如果没有对fseek、fsetpos或rewind的干预调用,输入后面不能跟着输出,除非输入操作遇到文件末尾。在更新模式下,即使指定了文本模式,也允许实现使用二进制模式。"r+"
2赞 Eric Postpischil 11/8/2023
你试过吗?也就是说,打开文件进行读取和写入(您不必读取)。您可能还需要根据您要进行的寻求类型。没有 ,只寻求开始、结束或获得的位置可能得到支持。r+rb+bftell
0赞 Fraser Orr 11/8/2023
是的。。。。“r+”可以完成这项工作(从说“我对 C 文件 IO 不太熟悉”😀的家伙那里还不错。如果你能把它放在答案中,我会接受,以便将来的搜索能找到它。

答:

2赞 Weather Vane 11/8/2023 #1

当您在“追加”模式下打开文件时,所有写入都会在文件末尾进行。

MSVC文档说:

使用访问类型或访问类型打开文件时,所有写入操作都发生在文件末尾。可以使用 或 重新定位文件指针,但在执行任何写入操作之前,始终将指针移回文件末尾。因此,无法覆盖现有数据"a""a+"fseekrewind

若要打开现有文件进行读取和写入,请使用该模式。"r+"

评论

2赞 Weather Vane 11/8/2023
虽然您可以在文本文件中,但这样做并覆盖那里的内容通常不是一件很有成效的事情。通常最好使用必要的更改重写到另一个文件。fseek
1赞 John Bollinger 11/8/2023
此外,“对文本流的写入是否会导致关联文件被截断超过该点是实现定义的”(C23 7.23.3/2)。这是一个相当大的问题,即使你最初编写的实现在这方面做了你想要的。
0赞 Weather Vane 11/8/2023
@JohnBollinger这是一个有趣的知识片段。
3赞 Joel 11/8/2023 #2

我希望测试文件 ttt 包含“12xxx67890”,但它包含“1234567890xxx”。(fseek 的结果为 0。

fseek() 不是问题。您仍然可以使用它重新定位指针,但每次在追加模式下写入都会将其设置为指向文件的末尾。

cppreference - fopen, fopen_s

在追加文件访问模式下,无论文件位置指示器的当前位置如何,数据都会写入文件末尾。

append 模式下的 fseek() 上查看这个问题。在用于读取文件和写入文件时,此行为非常有用。"a+"


要解决此问题,您可以使用 读取扩展模式 。这使您可以读取和写入文件,而无需关闭并重新打开它。"r+"

cppreference - fopen, fopen_s

In update mode (), both input and output may be performed, but output cannot be followed by input without an intervening call to fflush, fseek, fsetpos or rewind, and input cannot be followed by output without an intervening call to fseek, fsetpos or rewind, unless the input operation encountered end of file. In update mode, implementations are permitted to use binary mode even when text mode is specified.'+'

A working solution might look like this:

#include <stdio.h>

int main() {
    FILE* t = fopen("ttt", "w");

    fprintf(t, "1234567890");
    fclose(t);

    t = fopen("ttt", "r+");
    int result = fseek(t, 2, SEEK_SET);

    fprintf(stderr, "%d", result);
    fprintf(t, "xxx");

    fclose(t);
    return 0;
}

You could now be tempted to just use once at the start of your program since it can be used to perform both write actions you use but note that it will not create a new file if it does not exists yet."r+"