(C) 使用 while(fscanf(fptr,"%[^\n] %d %f",nome,&regAlmoxarifado,&preco) != EOF) 将程序锁

Posted

技术标签:

【中文标题】(C) 使用 while(fscanf(fptr,"%[^\\n] %d %f",nome,&regAlmoxarifado,&preco) != EOF) 将程序锁定在循环中【英文标题】:(C) Using while(fscanf(fptr,"%[^\n] %d %f",nome,&regAlmoxarifado,&preco) != EOF) locks the program into a loop(C) 使用 while(fscanf(fptr,"%[^\n] %d %f",nome,&regAlmoxarifado,&preco) != EOF) 将程序锁定在循环中 【发布时间】:2020-11-11 18:38:48 【问题描述】:

我有一个包含以下内容的文件:

lucas lucas lucas
24121 23.00
popotao popotao
2525252 23.52
yee yee
2442421 12.00

我必须读取所有单词并将它们全部存储在一个字符串中,然后将数字存储在 int 中,最后一个数字存储在 float 中。如果我只使用“%s %d %f”,它就可以正常工作,除了%s 只提取单词直到它找到一个空格,这意味着它在控制台中打印如下:

lucas 0 0.00
lucas 0 0.00
lucas 24121 23.00
popotao 24121 23.00
popotao 2525252 23.52
yee 2525252 23.52
yee 2442421 12.00

所以我尝试使用%[^\n](以及一些变体,例如[^\t\n][^\t\n]s\t 并没有什么不同,最后加上s 只会进一步破坏程序),它适用于前 3 个值(lucas lucas lucas 进入字符串,24121 进入 int 和 23.00 进入 float),但它只是不断地反复打印相同的 3 个值,如下所示:

lucas lucas lucas 24121 23.00
lucas lucas lucas 24121 23.00
lucas lucas lucas 24121 23.00
lucas lucas lucas 24121 23.00
lucas lucas lucas 24121 23.00
lucas lucas lucas 24121 23.00
lucas lucas lucas 24121 23.00

它没有读取以下内容,程序也没有结束,老实说我不知道​​是什么问题,使用调试工具变量“nome,regAlmoxarifado,preco”在通过 fscanf 时保持相同的值.这是实际代码,欢迎任何帮助:

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

int main(void)
    FILE *fptr;
    char nome[20];
    int regAlmoxarifado;
    float preco;
    fptr = fopen("Estoque.txt","r");
    
    while(fscanf(fptr,"%[^\n] %d %f",nome,&regAlmoxarifado,&preco) != EOF)
        printf("%s %d %.2f\n",nome,regAlmoxarifado,preco);
        
    fclose(fptr);
    return 0;

【问题讨论】:

我建议你用fgets() 阅读每一行并应用sscanf()。如果这些行的格式不同,请一一尝试格式,直到找到有效的格式。先尝试最难适应的。所以你会尝试%s%s 两个字符串,最后。 我建议您改用fgets 来读取行。并且您将所有输入读取为行,然后使用sscanf 解析该行。 OT:关于:fptr = fopen("Estoque.txt","r"); 始终检查 (!=NULL) 返回值以确保操作成功。如果不成功 (==NULL) 则调用 perror( "fopen to read Estoque.txt failed" ); 后跟:exit( EXIT_FAILURE ); OT: about: fscanf(fptr,"%[^\n] %d %f 1) 第一个格式转换说明符用于 20 个字符的字段,并且 %[] 总是将 NUL 字节附加到输入。因此,为了避免缓冲区溢出的任何可能性,需要有一个比输入缓冲区长度小 1 的 MAX CHARACTERS 修饰符。检查成功而不是EOF要好得多。建议while(fscanf(fptr,"%19[^\n] %d %f",nome, &amp;regAlmoxarifado, &amp;preco) == 3 ) 【参考方案1】:

在格式字符串的开头放置一个空格,以便在开始查找单词之前跳过空格。否则会失败,因为两个数字后面的下一个字符是换行符,%[^\n] 失败。

而不是与 EOF 比较,而是与 3 进行比较,这是您希望它扫描的元素数。这样解析失败时就不会陷入无限循环。

    while(fscanf(fptr," %[^\n] %d %f",nome,&regAlmoxarifado,&preco) == 3)

【讨论】:

@Barmer,我刚刚用您的 while 声明替换了 OPs while 声明,但屏幕上没有打印任何内容。 糟糕,!= 现在应该是 == @Barmar 谢谢你,这很有效,这样一个简单的修复哈哈。但是,将其更改为 3 对我的目的不起作用,其想法是该文件从另一个记录名称的文件中读取,它们可以有 1、2、3 或者一个名称可以有多少个单词,并不总是相同的数量。不过谢谢你的建议,我会记住的。 不计算字数。它正在计算从输入中提取的项目数。 %[^\n]提取的所有单词都算作1项。 所以它是 1 行 + 2 个浮点数 = 3。【参考方案2】:

尝试改变

while(fscanf(fptr,"%[^\n] %d %f",nome,&regAlmoxarifado,&preco) != EOF)

while(fscanf(fptr," %19[^0-9] %d %f",nome,&regAlmoxarifado,&preco) == 3)

%19[ 之前的空格,用于消耗上次扫描留下的尾随新行。

19 被保护免受缓冲区溢出(字符串的长度为 20,包括最后的 '\0')。

^0-9 读取直到找到一个数字。

比较 == 3 而不是 != EOF 以跳过格式错误的行。

或者更好的是,使用fgetsstring.h 中提供的功能。 scanf 总是一个糟糕的选择:

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

int main(void)

    FILE *file;
    char str[128];
    long reg;
    double pre;

    file = fopen("Estoque.txt","r");
    while (fgets(str, sizeof str, file))
    
        size_t pos;

        if ((pos = strcspn(str, "0123456789")) > 0) // Position of the first digit
        
            str[pos - 1] = '\0'; // Cut the string
        

        char *ptr;

        reg = strtol(str + pos, &ptr, 10); // 10 means base 10
        pre = strtod(ptr, NULL);
        printf("%s %ld %.2f\n", str, reg, pre);
    
    fclose(file);
    return 0;

【讨论】:

我一直想知道,当我们有更好的方法来使用fgets 读取字符串时,为什么要引入这些转义字符。我看到大多数人都在使用这种方式来阅读多字串 @csavvy 许多初学者认为scanf 是一个通用的输入解析器,尽管它并没有他们期望的那么有用。

以上是关于(C) 使用 while(fscanf(fptr,"%[^\n] %d %f",nome,&regAlmoxarifado,&preco) != EOF) 将程序锁的主要内容,如果未能解决你的问题,请参考以下文章

C中的fscanf - 如何确定逗号?

使用 fscanf 时防止缓冲区溢出

如何让fscanf自动在文件读结束时候停止执行

关于 fscanf_s 的 bug

关于 fscanf_s 的 bug

关于 fscanf_s 的 bug