为什么在rb+模式下文件中间的读写转换失败,除非我使用fseek(fp,0,SEEK_CUR)?为什么在文件末尾可以正常工作?

3

我从未意识到这一点。我可能会下意识地认为,在打开更新模式的现有文件上,我可以自如地在阅读和写作之间进行转换。但是,两个关于此问题的SO问题(12)让我产生了怀疑,于是我决定尝试一下。以下是我的发现:

在第一个程序prog1中,我处理一个名为source.txt的文件,该文件只有一行Lethal weapon。我使用fscanf()读取第一个单词Lethal,并打算在其后面写入" musket",期望得到Lethal musket。但它失败了,我最终仍然得到原始内容Lethal weapon。但是,在那个第一个程序中,如果我插入fseek(fp,0,SEEK_CUR)这一行,写操作就能正常工作,我会得到Lethal musket。我注意到fseek(fp,0,SEEK_CUR)除了纯粹调用fseek()之外没有任何作用,因为根本没有实际的寻找。

但是在第二个程序prog2中,同样的情况不需要fseek(fp,0,SEEK_CUR)语句。确切地说,在第二个程序中,与第一个程序读取文件中间相反,我在读到文件结尾后开始写入。即使没有使用fseek(fp,0,SEEK_CUR),这里的写入也是成功的,并且我得到了所需的内容Lethal weapon sale

问题:为什么我们不能在文件中间从read模式转换为write模式,并且fseek(fp,0,SEEK_CUR)有何区别可以让它正常工作?同时,为什么如果我们读取到文件结尾并在那里写入,相同的转换就可以顺利完成,而不需要使用fseek(fp,0,SEEK_CUR)?在第一个程序中,使用fseek(fp,0,SEEK_CUR)使写入成功是否明智?是否有更好的替代方法?

有两个stackoverflow问题在某种程度上解决了同一个问题,但由于它们更多地基于寻求文本/书籍摘录的解释,答案偏向于它们似乎没有精确回答我想要知道的内容。

//PROG1

#include <stdio.h>

int main ()
{
    FILE *fp=fopen("D:\\source.txt","r+");
    char arr[20],brr[50];
    fscanf(fp,"%s",arr);
    printf("String is %s\n",arr);
    //fseek(fp,0,SEEK_CUR);  //Doesn't work without it
    fprintf(fp," musket");
    rewind(fp);
    fgets(brr,50,fp);
    printf("New string is %s",brr);
    fclose(fp);
}

输出:

1) 没有使用fseek() -- 致命武器

2) 使用fseek() -- 致命火枪

//PROG2    

#include <stdio.h>

int main ()
{
    FILE *fp=fopen("D:\\source.txt","r+");
    char arr[20],brr[50];
    fgets(arr,20,fp);
    printf("Initial line is %s\n",arr);
    fprintf(fp," sale"); //writes to end of file
    rewind(fp);
    printf("New string is %s",fgets(brr,50,fp));
    fclose(fp);
}

不使用 fseek() 的输出: -- 致命武器销售


模式 'b' 是二进制模式,通常用于读取时使用 fread,写入时使用 fwrite。我尝试了你的代码,但行为不同。 - MYMNeo
@MYMNeo 因为我没有使用数字,所以二进制模式在这里不是问题。但我同意使用文本模式来说明我的观点更好。正在编辑中。感谢您指出。顺便问一下,在你的电脑上有什么反应? - Rüppell's Vulture
@MYMNeo 我仔细检查了一下,在我的电脑上没有使用 fseek() 仍然是 致命武器,这似乎与其他两个问题及其链接所说的相符。 - Rüppell's Vulture
@MYMNeo 看起来我们都迷路了MYMNeo。让我们等待并观看更聪明的人如何回答。希望在我设置悬赏之前得到答案。 - Rüppell's Vulture
为什么第二个不需要fseek(),我认为关键在于你引用的最后一句话:“_读取操作没有到达文件结尾,接着是写入操作_”。在第二种情况下,读取已经到达了文件结尾,因此不需要刷新/ fseek。对于第一个示例中的差异,这将取决于实现:某些编译器/平台/操作系统将需要fseek / flush,但在某些情况下可能会无需它(但是,由于未定义其工作方式,因此仍应包括它)。 - TripeHound
显示剩余4条评论
1个回答

0

实际上,这看起来像是您的libc实现中的一个错误。 文件I/O流通常是基于操作系统内核实现的文件描述符二进制I/O的libc抽象。因此,任何奇怪的行为都应归因于您特定的libc怪癖。

由于您显然正在使用Windows,这可能是您问题的根源。您使用的编译器是什么? 在Ubuntu 11.10上,GCC 4.6.1和glibc-2.13没有这样的问题。


我在Windows上使用CodeBlocks/gcc。 - Rüppell's Vulture
并且引用自 cplusplusreference 的话说:对于打开了更新模式(包含 "+" 符号)的文件,允许进行输入和输出操作,应该在写入操作后跟读取操作之间或未到达文件结尾的读取操作后跟写入操作之间刷新流(fflush)或重新定位(fseek、fsetpos、rewind)。 - Rüppell's Vulture

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