我知道这里使用的%d格式说明符读取一个整数,忽略它前面的空白,包括换行符(我已经验证过了),但是在下面的程序中,使用fscanf()读取一个多行文件,每个文件有3个整数,格式字符串fscanf()的工作效果与"%d%d%d%*c"一样好。
为什么会这样?由于"%d%d%d"fscanf()一起用作格式说明符字符串中的第一个格式说明符时会忽略整数前面的任何空格,为什么用作最后一个说明符的多余的%d不会导致任何错误或副作用?如果%*c说明符没有忽略一行中每组3个数字后面的换行符,那么%d就有意义了,因为它会吃掉换行符。但是为什么即使默认情况下%*c忽略fscanf()的空白,它也不会出错或产生副作用呢?当%*c找不到要吃的字符并且说明符和输入不匹配时,fscanf()不应该停止扫描吗?当出现不匹配时%d不应该停止吗,就像fscanf()那样?
编辑:即使我使用scanf(),它也能工作!!一旦格式说明符和开头的输入不匹配,后续字符的扫描和处理是否应该停止?

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


int main ()
{
int n1,n2,n3;
FILE *fp;
fp=fopen("D:\\data.txt","r");

if(fp==NULL)
{
printf("Error");
exit(-1);
}

while(fscanf(fp,"%d%d%d%*c",&n1,&n2,&n3)!=EOF) //Works as good as line below
//while(fscanf(fp,"%d%d%d",&n1,&n2,&n3)!=EOF)
printf("%d,%d,%d\n",n1,n2,n3);
fclose(fp);

}

以下是我的文件中数据的格式:
243 343 434
393 322 439
984 143 943
438 243 938

输出:
243 343 434
393 322 439
984 143 943
438 243 938

最佳答案

考虑问题中程序的这种变化:

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

int main(int argc, char **argv)
{
    char *file = "D:\\data.txt";
    FILE *fp;
    char *formats[] =
    {
    "%d%d%d%*c",
    "%d%d%d",
    "%*c%d%d%d",
    };

    if (argc > 1)
        file = argv[1];

    for (int i = 0; i < 3; i++)
    {
        if ((fp = fopen(file, "r")) == 0)
        {
            fprintf(stderr, "Failed to open file %s\n", file);
            break;
        }
        printf("Format: %s\n", formats[i]);
        int n1,n2,n3;
        while (fscanf(fp, formats[i], &n1, &n2, &n3) == 3)
            printf("%d, %d, %d\n", n1, n2, n3);
        fclose(fp);
    }
    return 0;
}

重复的开放是没有效率的,但这不是一个问题在这里。更重要的是要清楚地表明自己的行为。
它被写入(a)使用命令行中指定的文件名,这样我就不必使用在Unix系统上创建非常不方便的名称,比如D:\data.txt,和(b)显示正在使用的三种格式。
给定问题中的数据文件:
243 343 434
393 322 439
984 143 943
438 243 938

程序的输出是:
Format: %d%d%d%*c
243, 343, 434
393, 322, 439
984, 143, 943
438, 243, 938
Format: %d%d%d
243, 343, 434
393, 322, 439
984, 143, 943
438, 243, 938
Format: %*c%d%d%d
43, 343, 434
393, 322, 439
984, 143, 943
438, 243, 938

注意,当第一个数字是格式的第一部分时,%*c将使用第一个数字的第一个数字。读取前3个数字后,%*c读取第3个数字后的换行符,然后%d跳过更多的空白(除了没有空白)并读取该数字。
否则,这种行为如下文评注所述,基本上是从另一个相关问题中提出来的。
在相关问题Use fscanf() to read from given line中讨论的一些代码是:
fscanf(f, "%*d %*d %*d%*c");
fscanf(f, "%d%d%d", &num1, &num2, &num3);

我注意到代码应该测试fscanf()的返回值。但是,对于三个%*d转换规范,如果在到达指定行之前遇到EOF,则可能会得到一个返回值EOF。不幸的是,直到执行第二行fscanf(),您才知道第一行包含字母而不是数字。您还应该测试第二个fscanf();您可能会得到EOF、0、1或2(所有这些都表示有问题),或者您可能会得到3,表示3次转换成功。请注意,向格式中添加\n意味着将跳过空行,但无论如何都会发生这种情况;%d将空格跳到第一个数字。
有没有其他方法可以让我们阅读,但忽略整行,就像我笨拙地使用fscanf(f,"%*d%*d%*d")?使用%*[^\n]是最接近的方法吗?
跳过整行代码的最好方法是使用fgets(),就像我answer中最后一个版本的代码一样。显然,如果其中任何一行的长度超过4095字节,它都有可能会错误计数行。哦,那是不可能的。
我现在有点困惑,我不想问这个问题。所以你能告诉我-fscanf()自动忽略空白,所以在第一行之后,当根据我的%*d%*d%*d说明符读取并忽略三个整数时,我希望fscanf()在下一次循环中开始读取时也忽略换行符。但是为什么当我在代码中使用%*c\n时,附加的%*d%*d%*d%*c%*d%*d%*d\n不会导致问题,并且程序运行良好?
您无法判断这些格式哪里出了问题;您可以检测到EOF,但否则,fscanf()将返回0。但是,由于%*d跳过了前导空格(包括换行符),所以在第三个数字后面是否用%*c读取换行符并不重要,当您在第三个数字后面有\n时,这是一个空格,所以读取跳过了换行符和任何尾随或前导空格,当它到达非空格字符时停止。当然,你也可以在三个数字中间加上新行,或者一行有三个以上的数字。
注意,当用户在终端输入时,格式中的尾部\n特别奇怪。用户点击return,并继续点击return,但是直到用户键入一个非空字符,程序才会继续。这就是为什么当数据不可靠时,fscanf()很难使用的原因。当它是可靠的,这很容易,但如果有什么问题,诊断和恢复是痛苦的。这就是为什么最好使用fgets()sscanf();您可以控制正在解析的内容,如果需要,可以使用不同的格式重试,并且可以报告整行内容,而不仅仅是fscanf()未能解释的内容。
注意%c(和%*c)不会跳过空白;因此,格式末尾的%*c会读取(并丢弃)读取的数字后面的字符。如果这是换行符,那么这就是读取并忽略的字符。扫描集%[...]是另一个不跳过空白的转换规范;所有其他标准转换规范都跳过前导空白。

关于c - 使用fscanf()读取每行3个数字的文件,为什么“%d%d%d%* c”和“%d%d%d”一样好?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16557997/

10-13 06:29