我想用“5”修改一个文件的一些元音。下面的代码可以工作。但是,我不明白为什么要把fseek放两次。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void print_file_contents(const char *filename)
{
FILE *fp;
char letter;
if((fp=fopen(filename,"r+"))==NULL)
{
printf("error\n");
exit(1);
}
fseek(fp,0,SEEK_END);
int size=ftell(fp);
rewind(fp);
for(int i=0;i<size;i++)
{
fseek(fp,i,SEEK_SET);
letter=fgetc(fp);
if((letter=='a') || (letter=='e') || (letter=='i'))
{
fseek(fp,i,SEEK_SET); // WHY THIS FSEEK ?
fwrite("5",1,sizeof(char),fp);
}
}
fclose(fp);
}
int main(int argc, char *argv[])
{
print_file_contents("myfile");
return 0;
}
在我看来,第一个fseek(fp, i, SEEK_SET)
用于将文件位置指示符设置为当前正在处理的字符,以便可以使用fgetc
读取该字符,因此,每次都会更新光标,因此不需要添加另一个fseek(fp, i, SEEK_SET);
。
4条答案
按热度按时间nsc4cvqm1#
fgetc
推进了文件位置;如果你想替换你刚读到的字符,你需要倒带到你读到要替换的字符时所在的位置。ttcibm8c2#
请注意,当您在阅读和写之间(以及在写和读之间)切换时,C标准强制执行类似于seek的操作。
§7.21.5.s
fopen
函数¶7:¶7当文件以更新模式打开时(“
+
”作为上述模式参数值列表中的第二或第三个字符),则输入和输出都可以在关联流上执行。但是,在没有对fflush
函数或文件定位函数的中间调用的情况下,输出之后不应直接跟随输入(fseek
、fsetpos
或rewind
),并且除非输入操作遇到文件结束,否则在没有对文件定位函数的中间调用的情况下,输入之后不应直接跟随输出。此外,调用
fgetc()
会将文件位置向前移动一个字符;如果写操作成功(如果省略了类似seek的操作,这是未定义的行为),则将覆盖下一个字符,而不是刚刚读取的字符。pgky5nke3#
你的直觉是正确的:本程序中三个
fseek
调用中的 * 两个 * 是不必要的。必要的
fseek
是if((letter=='a') || (letter=='e') || (letter=='i'))
条件中的一个,它用来备份文件位置,这样你就可以覆盖你刚刚读到的字符(即元音),而不是元音后面的字符。循环内部的
fseek
(但在if
之外)是不必要的,因为fgetc
和fwrite
都将文件位置前移,所以它总是将文件位置设置为它已经拥有的位置;循环之前的fseek
是不必要的,因为您不需要知道文件有多大就可以实现此算法。这段代码可以被压缩得很紧,我会这样写:
注:
1.通常(但不总是)最好将具有副作用的操作(如
fopen
)与检查它们是否成功的条件分开编写。1.当系统级操作失败时,总是打印所涉及的任何文件的名称和
errno
的解码值。perror(filename)
是一种方便的方法。1.你不需要知道你正在处理的文件的大小,因为你可以使用这样的循环,这也是(1)的一个例外。
1.为什么
'o'
和'u'
也不行呢?1.下面是对fseek的必要调用,以及不需要知道文件大小的 * 其他 * 原因:你可以使用
SEEK_CUR
来备份一个字符。1.这个
fflush
是必要的,因为我们正在从写切换到读,正如Jonathan Leffler的回答中所述,它还消耗了一些(但不是全部)I/O错误的通知,因此您必须检查它是否失败。1.因为您正在写入文件,所以还必须检查 * delayed * I/O错误,这些错误仅在
fclose
上报告(这是操作系统中的一个设计错误,但我们永远无法摆脱)。1.最佳实践是在命令行上将文件名传递给munge,而不是将其硬编码到程序中。
x6h2sr284#
@Jonathan Leffler很好地说明了代码使用多个
fseek()
的原因:以科普阅读和写作之间的变化。int size=ftell(fp);
较弱,因为从ftell()
返回的值的范围是long
。在文本文件中查找(如OP)也会有未定义行为(UB)的风险。
对于一个文本流,偏移量应该为零,或者偏移量应该是先前成功调用同一文件相关流上的ftell函数所返回的值,因此应该是
SEEK_SET
. C17 dr §7.21.9.13.最好使用@zwol之类的方法,做一个小的改动。
不要假设一个平滑的线性Map。相反,注意位置,然后根据需要返回到它。
研究
fgetpos(), fsetpos()
,寻找一个更好的解决方案,可以处理所有大小的文件,甚至比LONG_MAX
更长的文件。