修改c中文件的现有内容
int main() { FILE *ft; char ch; ft=fopen("abc.txt","r+"); if(ft==NULL) { printf("can not open target file\n"); exit(1); } while(1) { ch=fgetc(ft); if(ch==EOF) { printf("done"); break; } if(ch=='i') { fputc('a',ft); } } fclose(ft); return 0; }
正如我可以看到的那样,我想以这样一种方式编辑abc.txt
,即i
将其替换为a
。
该程序工作正常,但当我在外部打开abc.txt
,它似乎是未经编辑的。
任何可能的原因?
为什么在这种情况下, i
后面的字符不能替换为a
,如答案所示?
分析
有很多问题:
-
fgetc()
返回一个int
,而不是一个char
; 它必须返回每个有效的char
值加上一个单独的值EOF。 如上所述,您无法可靠地检测到EOF。 如果char
是无符号类型,则永远不会找到EOF; 如果char
是签名类型,你将错误识别一些有效字符(通常是ÿ,y-umlaut,U + 00FF,带有DIAERESIS的LATIN SMALL LET)作为EOF。 -
如果在为更新模式打开的文件上切换输入和输出,则必须在读取和写入之间使用文件定位操作(
fseek()
,rewind()
,名义上为fsetpos()
); 并且您必须在写入和读取之间使用定位操作或fflush()
。 -
关闭你打开的东西是个好主意(现在修复了代码)。
-
如果你的写作有效,你会用
i
覆盖i
之后的字符。
合成
这些变化导致:
#include #include int main(void) { FILE *ft; char const *name = "abc.txt"; int ch; ft = fopen(name, "r+"); if (ft == NULL) { fprintf(stderr, "cannot open target file %s\n", name); exit(1); } while ((ch = fgetc(ft)) != EOF) { if (ch == 'i') { fseek(ft, -1, SEEK_CUR); fputc('a',ft); fseek(ft, 0, SEEK_CUR); } } fclose(ft); return 0; }
有更多错误检查的余地。
注释
输入后跟输出需要搜索
fseek(ft, 0, SEEK_CUR);
声明是C标准所要求的。
ISO / IEC 9899:2011§7.21.5.3fopen函数
¶7使用更新模式打开文件时(’+’作为上述模式参数值列表中的第二个或第三个字符),可以在关联的流上执行输入和输出。 但是,输入不应直接跟随输入而不干涉
fflush
function或文件定位function(fseek
,fsetpos
或rewind
),并且输入不应直接跟随输出而不干预文件定位函数,除非输入操作遇到文件结尾。 在某些实现中,打开(或创建)具有更新模式的文本文件可以改为打开(或创建)二进制流。
(重点补充。)
fgetc()
返回一个int
ISO / IEC 9899:2011的引用,是目前的C标准。
§7.21输入/输出
§7.21.1简介
EOF
扩展为整数常量表达式,类型为int和负值,由多个函数返回以指示文件结束,即不再有来自流的输入;§7.21.7.1fgetc函数
int fgetc(FILE *stream);
¶2如果没有设置stream指向的输入流的文件结束指示符并且存在下一个字符,则
fgetc
函数将该字符作为转换为int
的unsigned char
获取并提前关联的文件位置指示符流(如果已定义)。返回
¶3如果设置了流的文件结束指示符,或者流位于文件结尾,则设置流的文件结束指示符,并且
fgetc
函数返回EOF。 否则,fgetc
函数返回stream指向的输入流中的下一个字符。 如果发生读取错误,则设置流的错误指示符,并且fgetc
函数返回EOF。 289)289)可以通过使用
feof
和ferror
函数来区分文件结束和读取错误。
因此, EOF
是一个负整数(通常它是-1,但标准不要求这样)。 fgetc()
函数返回EOF或字符值作为unsigned char
(在0..UCHAR_MAX范围内,通常为0..255)。
§6.2.5类型
¶3声明为
char
类型的对象足以存储基本执行字符集的任何成员。 如果基本执行字符集的成员存储在char
对象中,则其值保证为非负值。 如果任何其他字符存储在char
对象中,则结果值是实现定义的,但应在可以在该类型中表示的值的范围内。¶5声明为
signed char
类型的对象占用与”plain”char对象相同的存储量。§6对于每个有符号整数类型,有一个相应的(但不同的)无符号整数类型(用关键字
unsigned
指定),它使用相同数量的存储(包括符号信息)并具有相同的对齐要求。§15cons,
signed char
和unsigned char
这三种类型统称为字符类型。 实现应将char
定义为具有与signed char
或unsigned char
相同的范围,表示和行为。 45)45)在
定义的
CHAR_MIN
将具有值0
或SCHAR_MIN
,这可用于区分这两个选项。 无论做出何种选择,char
都是与其他两种类型不同的类型,并且与两者都不兼容。
这certificate了我断言普通char
可以是有符号或无符号类型。
现在考虑:
char c = fgetc(fp); if (c == EOF) …
假设fgetc()
返回EOF,而plain char
是无符号(8位)类型,EOF是-1
。 赋值将值0xFF放入c
,这是一个正整数。 进行比较时, c
被提升为int
(因此值为255),255不是负数,因此比较失败。
相反,假设plain char
是带符号(8位)类型,字符集是ISO 8859-15。 如果fgetc()
返回ÿ,则指定的值将是位模式0b11111111,它与-1
相同,因此在比较中, c
将转换为-1
并且比较c == EOF
将返回true,即使a有效的字符被阅读。
您可以调整细节,但基本参数在sizeof(char) < sizeof(int)
仍然有效。 有些DSP芯片不适用; 你必须重新考虑规则。 即便如此,基本点仍然存在; fgetc()
返回一个int
,而不是一个char
。
如果您的数据是真正的ASCII(7位数据),那么所有字符都在0..127的范围内,您不会遇到对ÿ问题的误解。 但是,如果您的char
类型未签名,则仍然存在“无法检测到EOF”问题,因此您的程序将运行很长时间。 如果您需要考虑可移植性,则需要考虑这一点。 这些是作为C程序员需要处理的专业级问题。 您可以相对轻松地利用自己的方式处理系统中适用于您的数据的程序,而无需考虑所有这些细微差别。 但是你的程序不适用于其他人的系统。
你没有改变abc.txt
的’i’,你正在改变’i’之后的下一个字符。 尝试把fseek(ft, -1, SEEK_CUR);
在你的fputc('a', ft);
之前fputc('a', ft);
。
读完’i’字符后, ft
的文件位置指示符将是此’i’后面的字符,当你用fputc()
写字符时,该字符将写入当前文件位置,即字符在’我’之后。 有关详细信息,请参阅fseek(3)
。
阅读’i’后,您需要“退后一步”写入正确的位置。
if(ch=='i') { fseek(ft, -1, SEEK_CUR); fputc('a',ft); }