2
#include <stdio.h>

int main()
{
    FILE* f=fopen("book2.txt","r");
    char a[200];
    while(!feof(f))
    {
        fscanf(f,"%s",a);
        printf("%s ",a);
        printf("%d\n",ftell(f));
    }
    fclose(f);
    return 0;
}   

I have the code above. book2.txt contains "abcdef abcdef" with the cursor move to a newline(ie:abcdef abcdef\n). I get the results below.

abcdef 6
abcdef 13
abcdef 19

I expect to get

abcdef 6
abcdef 13
15

What am I doing wrong?

4

2 回答 2

4

测试 I/O 操作本身;除非您正在编写 Pascal,否则不要使用feof()(并且 C 不是 Pascal!)

#include <stdio.h>

int main(void)
{
    FILE *f = fopen("book2.txt", "r");
    char a[200];
    if (f != 0)
    {
        while (fscanf(f, "%199s", a) == 1)
        {
            printf("%s ", a);
            printf("%ld\n", ftell(f));
        }
        putchar('\n');
        fclose(f);
    }
    return 0;
}

请注意,修改后的代码f在使用之前会进行测试,并通过指定字符串在转换规范中的长度来防止缓冲区溢出。请注意,%s最多读取一个空格字符;除非每行没有空格,否则它不会读取行。

您用于feof()区分转换失败、I/O 错误和操作后的 EOF,例如fscanf()报告故障。例如:

#include <stdio.h>

int main(void)
{
    FILE *f = fopen("book2.txt", "r");
    char a[200];
    if (f != 0)
    {
        while (fscanf(f, "%199s", a) == 1)
        {
            printf("%s ",a);
            printf("%ld\n", ftell(f));
        }
        putchar('\n');
        if (feof(f))
            printf("EOF\n");
        else if (ferror(f))
            printf("I/O error\n");
        else
            printf("Conversion failed\n");
        fclose(f);
    }
    return 0;
}

使用%s,您将不会出现转换失败,并且 I/O 错误也不太可能发生。但是,如果转换说明符是%d,则数据中的标点符号可能会让您收到“转换失败”。

在超过 25 年的 C 编码中,我的代码中有 2 个地方使用了数千个文件(几个月前feof()我进行了检查),在这两种情况下,代码都区分了 EOF 和错误(大致如图所示)。

于 2013-04-07T05:16:30.057 回答
2

feof在您到达文件末尾之前,不会告诉您您已经到达文件末尾。从 .检查结果(返回值)fscanf。它会告诉您何时无法再读取。如果fscanf到达文件末尾,它将返回 EOF(并feof在发生这种情况后返回 true)。

现在只是为了好玩,看看这种方式。永远不要这样做。

while(1) {
    switch(fscanf(f,"%s",a)){
        case 1: 
            printf("%s ",a);
            printf("%d\n",ftell(f));
            continue;
        case EOF:
            break;
    }
    break;
}

这种方式是错误的,因为它很聪明。再有一些情况,整个循环的行为将变得非常难以辨别(例如这个程序)。与您一起fscanf,您实际上只需要知道您是否获得了继续所需的所有数据。让下一个输入读取函数在找到 EOF 时对其进行处理。调用fscanf需要填充x个值,因此,

if (fscanf(f,"%d %d %d", &i, &j, &k) != 3)
    /* cannot continue. need i j k values for next computation */

然后做你的错误响应,或者退出循环,或者你的程序接下来需要做的任何事情。但如果fscanf返回 3 以外的任何值,则它不会读取 3 个值。

于 2013-04-07T05:11:27.613 回答