修改c中文件的现有内容

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

分析

有很多问题:

  1. fgetc()返回int,而不是char; 它必须返回每个有效值char加上一个单独的值EOF.如上所述,您无法可靠地检测EOF.如果char是无符号类型,你永远不会找到EOF; 如果char是签名类型,你将错误识别一些有效字符(通常是ÿ,y-umlaut,U + 00FF,带有DIAERESIS的LATIN SMALL LET)作为EOF.

  2. 如果您在打开更新模式文件的输入和输出之间进行切换,你必须使用一个文件定位操作(fseek(),rewind()名义上fsetpos())阅读和写作之间; 你必须使用定位操作或fflush()写作和阅读之间.

  3. 关闭你打开的东西(现在在代码中修复)是个好主意.

  4. 如果您的写入有效,则在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() 返回一个 int

ISO/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)可以通过使用feofferror函数来区分文件结束和读取错误.

因此,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 charunsigned char.45)

45) CHAR_MIN,定义在<limits.h>,将有一个值0SCHAR_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程序员需要处理的专业级问题.您可以相对轻松地利用自己的方式处理系统中适用于您的数据的程序,而无需考虑所有这些细微差别.但是你的程序不适用于其他人的系统.

  • @OregonTrail:_au contraire_.C标准要求在更新流上的读取和写入操作之间或者在写入和读取之间进行定位操作.这是写入和读取之间的定位操作.它不是**无操作; 它将流放入一种模式,允许下一个`fgetc()`按照C标准的要求在平台上正确,可靠地工作. (3认同)
  • @OregonTrail检查[this](http://linux.die.net/man/3/fopen),它说:“请注意,ANSI C要求在输出和输入之间插入文件定位功能,除非输入操作遇到结束文件。” (2认同)
  • @zee`EOF`不是一个角色!因此,它必须超出`char`的范围*.这是一个值,表示不能从流中读取更多字符. (2认同)
  • @zee:在您的原始代码中,您没有执行“fseek()”操作。如果您检查“fputc()”的返回值,您可能会收到错误指示。另请参阅我的(广泛的)更新和“注释”部分。标准没有说明“fseek(ft, 0, SEEK_CUR)”如何允许“fgetc()”正常工作;它只是说它必须这样做。如何实施是一个问题。 (2认同)