zee*_*zee 6 c edit file-handling
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;
}
Run Code Online (Sandbox Code Playgroud)
正如我可以看到的那样,我想以abc.txt一种i被其替换的方式进行编辑a.
该程序工作正常,但当我在abc.txt外部打开时,它似乎是未经编辑的.
任何可能的原因?
为什么在这种情况下,后面的字符i不会被替换a,如答案所示?
Jon*_*ler 18
有很多问题:
fgetc()返回int,而不是char; 它必须返回每个有效值char加上一个单独的值EOF.如上所述,您无法可靠地检测EOF.如果char是无符号类型,你永远不会找到EOF; 如果char是签名类型,你将错误识别一些有效字符(通常是ÿ,y-umlaut,U + 00FF,带有DIAERESIS的LATIN SMALL LET)作为EOF.
如果您在打开更新模式文件的输入和输出之间进行切换,你必须使用一个文件定位操作(fseek(),rewind()名义上fsetpos())阅读和写作之间; 你必须使用定位操作或fflush()写作和阅读之间.
关闭你打开的东西(现在在代码中修复)是个好主意.
如果您的写入有效,则在i使用后覆盖该字符a.
这些变化导致:
#include <stdio.h>
#include <stdlib.h>
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;
}
Run Code Online (Sandbox Code Playgroud)
有更多错误检查的余地.
该fseek(ft, 0, SEEK_CUR);标准是C标准所要求的.
ISO/IEC 9899:2011§7.21.5.3该
fopen功能7使用更新模式打开文件时('+'作为上述模式参数值列表中的第二个或第三个字符),可以在关联的流上执行输入和输出.然而,输出不应直接跟随输入而没有插入调用
fflush功能或文件定位功能(fseek,fsetpos,或rewind),并且输入不应直接跟随输出而没有中间呼叫到一个文件中的定位功能,除非输入操作遇到文件结尾.在某些实现中,打开(或创建)具有更新模式的文本文件可以改为打开(或创建)二进制流.
(重点补充.)
fgetc() 返回一个 intISO/IEC 9899:2011引用,当前的C标准.
§7.21输入/输出
<stdio.h>§7.21.1简介
EOF它扩展为一个整数常量表达式,类型为int和负值,由几个函数返回以指示文件结束,即不再有来自流的输入;§7.21.7.1
fgetc功能
int fgetc(FILE *stream);2如果没有设置stream指向的输入流的文件结束指示符并且存在下一个字符,则该
fgetc函数将该字符作为unsigned char转换为a获得int并提前该流的相关文件位置指示符(如果定义).返回
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声明为type的对象
char足以存储基本执行字符集的任何成员.如果基本执行字符集的成员存储在char对象中,则其值保证为非负值.如果任何其他字符存储在char对象中,则结果值是实现定义的,但应在可以在该类型中表示的值范围内.5声明为类型的对象
signed char占用与"普通"char对象相同的存储量.§6对于每个有符号整数类型,都有一个相应的(但不同的)无符号整数类型(用关键字指定
unsigned),它使用相同的存储量(包括符号信息)并具有相同的对齐要求.§15这三种类型
char,signed char以及unsigned char统称为字符类型.实现应定义char为具有相同的范围,表示和行为如任一signed char或unsigned char.45)45)
CHAR_MIN,定义在<limits.h>,将有一个值0或SCHAR_MIN,这可用于区分这两个选项.无论做出char何种选择,都是与其他两种类型不同的类型,并且与两者都不兼容.
这证明了我的断言,即plain char可以是有符号或无符号类型.
现在考虑:
char c = fgetc(fp);
if (c == EOF)
…
Run Code Online (Sandbox Code Playgroud)
假设fgetc()返回EOF,plain char是无符号(8位)类型,而EOF是-1.赋值将值0xFF放入c,这是一个正整数.进行比较时,c提升为a int(因此值为255),255不是负数,因此比较失败.
相反,假设plain char是带符号(8位)类型,字符集是ISO 8859-15.如果fgetc()返回ÿ,则分配的值将是位模式0b11111111,它与之相同-1,因此在比较中,c将转换为-1并且c == EOF即使读取了有效字符,比较也将返回true.
您可以调整细节,但基本参数仍然有效sizeof(char) < sizeof(int).有些DSP芯片不适用; 你必须重新考虑规则.即便如此,基本点仍然存在; fgetc()返回一个int,而不是一个char.
如果您的数据是真正的ASCII(7位数据),那么所有字符都在0..127范围内,您不会遇到对ÿ问题的误解.但是,如果您的char类型未签名,您仍然有"无法检测到EOF"问题,因此您的程序将运行很长时间.如果您需要考虑可移植性,则需要考虑这一点.这些是作为C程序员需要处理的专业级问题.您可以相对轻松地利用自己的方式处理系统中适用于您的数据的程序,而无需考虑所有这些细微差别.但是你的程序不适用于其他人的系统.