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

时间:2019-04-24 04:16:48

标签: c scanf

我正在从文件中读取具有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。 有关如何修复的任何建议?抱歉,格式不理想。

1 个答案:

答案 0 :(得分:1)

如果您仍然遇到困难,可以举一个简短的例子。查看您尝试的格式字符串,例如

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

您似乎想将第二和第四字段另存为字符串值,分别位于namestate中。

我立即怀疑您使用&name, &state是不正确的,因为假设您已声明namestate为字符数组,其大小足以容纳第二和第四字段中的数据, namestate已经是指针(请参见:C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)),因此不需要在变量名之前使用'&'。如果未将它们声明为字符数组(或未声明为数组的指针并分配了足够的内存),则由于类型不兼容,您的解析将始终失败。

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

    如果15个字段中的任何一个出现 matching input 失败,则
  1. 您的读取将失败;和
  2. 您只关心第2个字段和第4个字段-仅关心其中的两个字段就不必依赖于成功解析15个字段。

因此,您不必担心正确匹配15个字段,而只需要担心4个字段,而不必关心第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;
}

注意:使用 field-width 修饰符来保护{{1}和name与{{1} }。state的使用只是为了修剪%31[^|]末尾的strcspn,因此在输出之后,'\n'buf的值将打印在同一行上name。如果您不打印state,则完全不需要该调用来进行解析)

使用/输出示例

生成的最小数据与您的格式字符串匹配。解析第二和第四值将在bufbuf中产生所需的字符串,例如

name

仔细研究一下,如果您有任何疑问,请告诉我。