使用fscanf从输入文件中逐行读取

问题描述 投票:0回答:1

我正在读取一个由“|”分隔的15个功能的文件符号。我正在使用:while(fscanf(file, "%*d|%s|%*s|%s|%*d|%*s|%*d|%*s|%*s|%*f|%*f|%*s|%*s|%*f|%*f", &name, &state)==2)但是当我运行gdb时,我意识到它从未真正进入循环。我使用%*d/s,因为它告诉fscanf跳过这些值正确吗?我只想从输入中读取整行的2个值,即开头附近的两个%s。有关如何修复的任何建议?抱歉没有最佳格式。

c scanf
1个回答
1
投票

如果你仍然被卡住,一个简短的例子可能有所帮助。查看您尝试过的格式字符串,例如

"%*d|%s|%*s|%s|%*d|%*s|%*d|%*s|%*s|%*f|%*f|%*s|%*s|%*f|%*f"

您似乎想要将第2和第4个字段保存为namestate中的字符串值。

我立即怀疑你使用&name, &state是不正确的,因为,假设你已经宣布namestate作为足够大小的字符数组来保存第2和第4个字段中的数据,namestate已经是指针(参见:C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)),所以有在变量名之前不需要'&'。如果它们未被声明为字符数组(或作为指向数组的指针并分配了足够的内存),则由于类型不兼容,您的解析无论如何都会失败。

接下来,为什么用fgets()或POSIX getline()读取整行,然后用sscanf()解析而不是使用fscanf()这么重要:

  1. 如果15个字段中的任何一个字段存在任何匹配或输入失败,则您的读取将失败;和
  2. 你只关心第二和第四个字段 - 当你只关心其中两个字段时,没有必要让你的读取依赖于15个字段的成功解析。

因此,不必担心15个字段的正确匹配,您只需要担心4 - 您不关心第4个字段后的其余部分。

将一个小例子与随机生成的数据放在一起(根据数据需要调整缓冲区大小),您可以执行以下操作,将第2和第4个字段解析为字符串:

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

#define FLDW   32   /* max field width */
#define MAXC 1024   /* max chars in line */

int main (int argc, char **argv) {

    char buf[MAXC],     /* line buffer */
        name[FLDW],     /* storage for name */
        state[FLDW];    /* storage for state */

    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (buf, MAXC, fp)) { /* read each line of input */
        /* parse 2nd & 4th fields as strings - you don't care about rest */
        if (sscanf (buf, "%*d|%31[^|]|%*[^|]|%31[^|]", name, state) == 2) {
            buf[strcspn (buf, "\n")] = 0;   /* trim \n from buf */
            /* output line with parsed name and state to right */
            printf ("%s  =>  name: %s, state: %s\n", buf, name, state);
        }
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    return 0;
}

(注意:使用字段宽度修饰符来保护namestate的数组边界与%31[^|]。使用strcspn只是为了从'\n'的末尾修剪buf所以namestate值将打印在同一行以下输出buf。如果你不打印buf,那么根本不需要那个调用来解析)

示例使用/输出

生成的最小数据与格式字符串匹配。解析第2和第4个值将在namestate中产生所需的字符串,例如:

$ ./bin/fgetssscanf dat/field15pipes.txt
01|8a|0b|6c|82|1d|33|5e|4f|7.|0.|4g|3h|7.|5.  =>  name: 8a, state: 6c
01|9a|5b|0c|42|1d|93|3e|9f|8.|0.|5g|4h|6.|5.  =>  name: 9a, state: 0c
01|4a|5b|7c|22|0d|23|1e|1f|7.|2.|1g|5h|7.|7.  =>  name: 4a, state: 7c
01|8a|2b|5c|72|1d|53|6e|2f|1.|1.|8g|0h|7.|6.  =>  name: 8a, state: 5c
11|4a|6b|5c|92|2d|73|0e|6f|4.|2.|2g|7h|2.|4.  =>  name: 4a, state: 5c
01|2a|6b|0c|02|1d|83|0e|2f|5.|2.|9g|4h|3.|8.  =>  name: 2a, state: 0c
31|1a|0b|0c|72|2d|13|3e|3f|9.|0.|2g|5h|6.|9.  =>  name: 1a, state: 0c
01|8a|3b|7c|92|1d|93|3e|9f|6.|1.|4g|4h|8.|3.  =>  name: 8a, state: 7c
11|1a|4b|7c|42|2d|73|0e|5f|7.|0.|0g|5h|1.|7.  =>  name: 1a, state: 7c
21|8a|6b|9c|22|2d|23|2e|1f|9.|0.|1g|6h|6.|8.  =>  name: 8a, state: 9c

仔细看看,如果您有疑问,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.