【问题标题】:modify existing contents of file in c修改c中文件的现有内容
【发布时间】:2014-03-24 08:49:58
【问题描述】:
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 替换,正如答案所暗示的那样?

【问题讨论】:

  • 试试fflush()-ing 描述符也许...
  • fclose(ft) 在你返回之前。
  • fgetc() 返回int,而不是char;它必须返回每个有效的char 值加上一个单独的值 EOF。如前所述,您无法可靠地检测 EOF。如果char 是无符号类型,您将永远找不到EOF;如果 char 是有符号类型,您会将某些有效字符(通常是 ÿ、y-umlaut、U+00FF、带分音符号的拉丁小写字母 Y)误识别为 EOF。

标签: c edit file-handling


【解决方案1】:

分析

有多个问题:

  1. fgetc() 返回 int,而不是 char;它必须返回每个有效的char 值加上一个单独的值 EOF。如前所述,您无法可靠地检测 EOF。如果char 是无符号类型,您将永远找不到EOF;如果 char 是有符号类型,您会将某些有效字符(通常是 ÿ、y-umlaut、U+00FF、带分音符号的拉丁小写字母 Y)误识别为 EOF。

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

  3. 关闭您打开的内容是个好主意(现在已在代码中修复)。

  4. 如果您的写入有效,您将用a 覆盖i 之后的字符。

合成

这些变化导致:

#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;
}

还有更多的错误检查空间。

释经

输入后输出需要查找

fseek(ft, 0, SEEK_CUR); 语句是 C 标准所要求的。

ISO/IEC 9899:2011 §7.21.5.3 fopen 函数

¶7 当文件以更新模式打开时('+' 作为第二个或第三个字符 以上模式参数值列表),输入和输出都可以在 关联的流。 但是,输出后面不能直接跟没有输入的输入 干预对 fflush 函数或文件定位函数的调用 (fseek, fsetpos, or rewind), 输入后面不能直接跟输出 干预对文件定位函数的调用,除非输入操作遇到 end-of- 使用更新模式打开(或创建)文本文件可能会打开(或创建)一个 一些实现中的二进制流。

(已添加重点。)

fgetc() 返回 int

引自 ISO/IEC 9899:2011,当前的 C 标准。

§7.21 输入/输出&lt;stdio.h&gt;

§7.21.1 简介

EOF 扩展为一个整数常量表达式,类型为 int 和一个负值,即 由几个函数返回以指示文件结束,即不再有来自 流;

§7.21.7.1 fgetc 函数

int fgetc(FILE *stream);

¶2 如果未设置 stream 指向的输入流的文件结束指示符并且 如果出现下一个字符,fgetc 函数会将该字符作为 unsigned char 转换为 int 并推进相关的文件位置指示符 流(如果已定义)。

退货

¶3 如果设置了流的文件结束指示符,或者如果流处于文件结束位置,则设置流的文件结束指示符并且fgetc 函数返回EOF。否则,该 fgetc 函数从流指向的输入流中返回下一个字符。 如果发生读取错误,则设置流的错误指示器并使用 fgetc 函数 返回 EOF。289)

289) 使用feofferror 函数可以区分文件结束和读取错误。

所以,EOF 是一个负整数(通常是 -1,但标准不要求这样做)。 fgetc() 函数返回 EOF 或作为 unsigned char 的字符值(在 0..UCHAR_MAX 范围内,通常为 0..255)。

§6.2.5 类型

¶3 声明为 char 类型的对象大到足以存储基本的任何成员 执行字符集。如果基本执行字符集的成员存储在 char 对象,它的值保证为非负数。如果任何其他字符存储在 char 对象,结果值是实现定义的,但应在范围内 可以用该类型表示的值。

¶5 声明为 signed char 类型的对象占用与 “普通”char 对象。

§6 对于每个有符号整数类型,都有一个对应的(但不同的)无符号的 使用相同数量的整数类型(用关键字unsigned 指定) 存储(包括标志信息)并具有相同的对齐要求。

§15charsigned charunsigned char这三种类型统称为 字符类型。实现应定义char 具有相同的范围, 表示和行为为signed charunsigned char45)

45)CHAR_MIN,在&lt;limits.h&gt; 中定义,将具有0SCHAR_MIN 值之一,这可以是 用于区分这两个选项。无论做出何种选择,char 都是与 其他两个不兼容。

这证明了我的断言,即普通的 char 可以是有符号或无符号类型。

现在考虑:

char c = fgetc(fp);
if (c == EOF)
   …

假设fgetc() 返回EOF,而普通char 是无符号(8 位)类型,EOF 是-1。该赋值将值 0xFF 放入 c,这是一个正整数。进行比较时,c 被提升为 int(并因此提升为值 255),并且 255 不是负数,因此比较失败。

相反,假设普通 char 是有符号(8 位)类型,字符集是 ISO 8859-15。如果fgetc()返回ÿ,则分配的值将是位模式0b11111111,与-1相同,所以在比较中,c将转换为-1,比较c == EOF将返回true即使读取了有效字符。

您可以调整细节,但基本参数在sizeof(char) &lt; sizeof(int) 期间仍然有效。有些 DSP 芯片不适用;你必须重新考虑规则。即便如此,基本点仍然存在; fgetc() 返回 int,而不是 char

如果您的数据是真正的 ASCII(7 位数据),那么所有字符都在 0..127 范围内,您不会遇到对 ÿ 问题的误解。但是,如果您的char 类型是无符号的,您仍然会遇到“无法检测到EOF”问题,因此您的程序将运行很长时间。如果你需要考虑可移植性,你会考虑到这一点。这些是您作为 C 程序员需要处理的专业级问题。您可以相对轻松地使用在您的系统上为您的数据工作的程序,而无需考虑所有这些细微差别。但是您的程序无法在其他人的系统上运行。

【讨论】:

  • fseek(ft, 0, SEEK_CUR); 这行什么也没做,也不需要。
  • @OregonTrail:相反。 C 标准要求在更新流上的读取和写入操作之间或在写入和读取之间进行定位操作。这是写和读之间的定位操作。它不是无操作;根据 C 标准的要求,它将流置于允许下一个 fgetc() 跨平台正确、可靠地工作的模式。
  • @OregonTrail Check this out,它说“请注意,ANSI C 要求文件定位函数在输出和输入之间进行干预,除非输入操作遇到文件结尾。”
  • @ajay:第 7 版 Unix 在 1979 年只有 "r""w""a" 模式。但是,C 标准的第一版(1989 年)具有扩展模式( b 修饰符和+ 模式),我认为+ 模式更早可用。
  • @zee EOF 不是字符!因此,它必须超出char 的范围。这是一个表示无法从流中读取更多字符的值。
【解决方案2】:

您没有更改abc.txt 中的“i”,而是更改了“i”之后的下一个字符。尝试将fseek(ft, -1, SEEK_CUR); 放在您的fputc('a', ft); 之前。

当你读取一个'i'字符后,ft的文件位置指示符将是这个'i'之后的字符,当你用fputc()写一个字符时,这个字符将被写入当前文件位置,即'i'之后的字符。详情请见fseek(3)

【讨论】:

  • 如果我输入fseek(ft, -1, SEEK_CUR); ,则循环变为无限。
  • @zee 不,不会。
  • 哎呀对不起...这是另一个错误
  • @zee:因为标准说你需要它,而且因为你不这样做它就不起作用。你还需要多少理由?
  • @zee:一般来说,C 标准中有比较特殊的规定,因为如果没有规定,某些系统或其他系统将难以处理。举一个极端的例子,请参阅&lt;setjmp.h&gt; 中关于如何使用setjmp() 宏的限制。更接近主题的是,对文本文件(尾随空格、最后换行符)发生的事情有限制,这使得系统可以遵守原本无法遵守的标准。在这种情况下,我不确定所有的细节,但它使实现更容易。记住还有ungetc() 需要处理。
【解决方案3】:

读完“i”后,您需要“退后一步”写入正确的位置。

if(ch=='i')
{
    fseek(ft, -1, SEEK_CUR);
    fputc('a',ft);
}

【讨论】:

  • 根据 C 标准,您还需要在 fputc() 之后进行第二次 fseek() 操作 - 请参阅我对标准相关引用的回答。
猜你喜欢
  • 2012-01-23
  • 2011-02-02
  • 2023-03-03
  • 2014-06-13
  • 2010-12-09
  • 1970-01-01
  • 2020-12-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多