【问题标题】:Unexpected behavior of 'fread()', 'fwrite()' and 'fseek()'“fread()”、“fwrite()”和“fseek()”的意外行为
【发布时间】:2012-06-02 00:04:24
【问题描述】:

我编写了一个简单的 C 程序,它采用 .txt 文件并将所有空格替换为连字符。但是,程序进入了一个无限循环,结果是无穷无尽的连字符数组。

这是输入文件:

a b c d e f

这是进程崩溃后的文件:

a----------------------------------------------------------------------------
----------------------------------------... (continues thousands of times)... 

我猜是fread()fwrite()fseek()的意外行为的原因,或者我对这些功能的误解。这是我的代码:

#include <stdlib.h>
#include <stdio.h>

#define MAXBUF 1024

int main(void) {

    char buf[MAXBUF];
    FILE *fp;
    char c;
    char hyph = '-';

    printf("Enter file name:\n");
    fgets(buf, MAXBUF, stdin);
    sscanf(buf, "%s\n", buf);   /* trick to replace '\n' with '\0' */

    if ((fp = fopen(buf, "r+")) == NULL) {
        perror("Error");
        return EXIT_FAILURE;
    }

    fread(&c, 1, 1, fp);

    while (c != EOF) {
        if (c == ' ') {
            fseek(fp, -1, SEEK_CUR); /* rewind file position indicator to the position of the ' ' */
            fwrite(&hyph, 1, 1, fp); /* write '-' instead */
        }
        fread(&c, 1, 1, fp); /* read next character */
    }

    fclose(fp);

    return EXIT_SUCCESS;
}

这里有什么问题?

【问题讨论】:

  • 您使用的是什么操作系统?我编译并运行了你的程序,由于没有正确检查 EOF(如乔的回答中所述),它进入了一个无限循环,但是它不会创建一个无休止的连字符流。

标签: c stdio


【解决方案1】:

你有两个问题:

1) 您应该检查 fread 返回您请求的项目数,例如你得到一个 1 回来。

2) 然后您应该检查 feof(fp),而不是将您读取的字符与 EOF 进行比较。这将告诉您您的读取是否由于 EOF 或其他原因返回了较少/没有项目。

【讨论】:

  • 你是对的,但它并没有修复错误。修复它的是我在fwrite(&amp;hyph, 1, 1, fp); 行之后添加了fseek(fp, 1, SEEK_CUR); 行。似乎fwrite() 根本没有推进文件位置指示器,这与我的想法相反。所以我手动做了。现在它起作用了。还是谢谢。
  • 如果fwrite 没有推进文件位置,则有问题。来自the opengroup page:流的文件位置指示符(如果已定义)应提前成功写入的字节数。
【解决方案2】:

你有几个问题...

检查标准 C 库函数返回的类型以及返回值的含义。 std C 库将EOF 定义为整数 -1。由于完整的字符集是 256 个字符,并且 char 类型可以容纳 0 到 255(256 个差异值),因此有必要将 EOF 设为整数。

撇开所有的咆哮不谈...您还错误地检查了EOF

问题,详细说明:

你应该检查fread的返回值

if( fread(&c, 1, 1, fp) != 1 )
{
    // Handle the error
}

// `EOF` is the integer -1.  It will not fit in a char.  So, your while loop becomes endless unless you get a -1 in the data stream

// The "correct" way to do what you want to do is using the stdlib function feof(fp)
while( !feof( fp ) )
{
    if (c == ' ')
    {
        // You should check the value returned by fseek for errors
        fseek(fp, -1, SEEK_CUR); /* rewind file position indicator to the position of the ' ' */
        // You should check the value returned by fwrite for errors
        fwrite(&hyph, 1, 1, fp); /* write '-' instead */
    }

    if( fread(&c, 1, 1, fp) != 1 )
    {
        // Handle the error
    }
}

综上所述……在现代系统上一次读取一个字符是非常低效的。调整您的代码以一次读取一个已满的缓冲区,并一次将整个修改后的缓冲区写出。

【讨论】:

  • @JimR 他应该写"-",这是一个const char *,而不是'-',它只是一个const char
  • @jimR hyphchar,但 &amp;hyphchar *。因此,您不能将&amp;hyph 替换为'-'const char * ptr = &amp;hyph;const char * ptr = "-"; 是有效的语法。 const char * ptr = '-'; 无效。
  • @DavidM.Syzdek:我不争论这个。在我出现之前,这是他代码的一部分。我没有纠正它,因为他已经用&amp;hyph 解决了这个问题,我想我把他埋在足够的信息中而没有提到这一点。出于同样的原因,我真的很犹豫要提到效率低下,但因为我认为这是一个更大的长期问题......
【解决方案3】:

原因:

对于为更新而打开的文件(包含“+”号的文件),允许输入和输出操作,在读取操作之前应刷新(fflush)或重新定位(fseek、fsetpos、rewind)流在写操作之后。在读取操作之后的写入操作之前(只要该操作未到达文件末尾),应重新定位流(fseek、fsetpos、rewind)。

解决办法:

您应该添加“fflush(fp);”在 fwrite 行之后。

【讨论】:

  • 谢谢你,我没有在参考文档中注意到这个注释,我广泛使用r+b 模式同时执行 fwrite+fread 并且它在许多平台上为我工作了很长时间(linux ,mac,bsd,windows) (文件位置被我天真地假设更新了),但是有一次我从零开始 fwrite 内容,并且代码在 windows 构建上确实失败了......花了我一整天才找到这个小笔记.使用明确的fseek 提前fread 它再次运行良好。
猜你喜欢
  • 2017-07-07
  • 1970-01-01
  • 2015-08-12
  • 1970-01-01
  • 1970-01-01
  • 2020-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多