繁体   English   中英

如何使用 scanf 读取文本文件中的下一行

[英]How do I read the next line in a text file using scanf

我有一个作业,我需要使用 scanf 从文本文件中读取输入并将输入临时存储到结构中,最后使用 printf 打印。 我们不允许使用结构数组。

这是文本文件:

1010  Jose Manalo      3.5
2003  Luisa Santos     2.5
1008  Andres Espinosa  4.0

以下是给出的结构(String20 是一个 20 个字符的数组):

struct nameTag {
    String20 first;
    String20 last;
};

struct studentTag {
    int ID;
    struct nameTag name;
    float grade;
};

我的初始代码如下所示(studentInfo 是我的 studentTag 结构变量):

scanf("%d %s %s %f", &studentInfo.ID, studentInfo.name.first, studentInfo.name.last, &studentInfo.grade);
printf("%d %s %s %.1f", studentInfo.ID, studentInfo.name.first, studentInfo.name.last, studentInfo.grade);

这当然只读取和打印第一行

编辑(已解决):将 scanf 放入循环中直到达到 EOF 或 NULL 工作完美。 这是我所做的:

while(scanf("%d %s %s %f", &studentInfo.ID, studentInfo.name.first, studentInfo.name.last, &studentInfo.grade)!=NULL)
    printf("%d %s %s %.1f\n", studentInfo.ID, studentInfo.name.first, studentInfo.name.last, studentInfo.grade);

当您一次读取一行时,请使用面向行的输入函数,例如fgets()或 POSIX getline() 对于尝试单独使用scanf()读取文件的新 C 程序员来说,这避免了许多陷阱。

单独使用scanf()的问题很多,但归结为尝试读取后输入流中剩余的内容。 例如,当使用scanf()尝试读取整数值时,如果在数字之前遇到非数字,则会发生匹配失败并且此时停止从输入流中提取字符,将所有有问题的字符留在输入中流未读——除非您手动清空输入流中的字符,否则只是等待在下一次尝试读取时再次咬您。 在读取float作为最后一个值后, '\\n'留在未读的输入流中,如果在您下次读取时,您的格式说明符不会忽略前导空格,空格将作为您的输入。 (这只是冰山一角...)

因此,使用fgets()一次将整行读取到适当大小的字符数组中。 这样你一次消耗了整条线。 然后,您使用sscanf()从数据行中解析您需要的内容。 无论解析成功/失败,您都不会影响从文件中读取下一行数据。 数据的读取和值的解析不再耦合在单个操作中。

将各个部分放在一起,在您的情况下,您可以执行以下操作:

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

#define MAXC  1024
#define MAXNM   20

typedef char String20[MAXNM];   /* your typedef */

struct nameTag {
    String20 first;
    String20 last;
};

struct studentTag {
    int ID;
    struct nameTag name;
    float grade;
};

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

    char buf[MAXC];     /* buffer to hold each line */
    size_t n = 0;       /* counter for number of students */
    struct studentTag std[MAXNM] = {{ .ID = 0 }};
    /* 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;
    }

    /* read each line -- until array full of EOF reached */
    while (n < MAXNM && fgets (buf, MAXC, fp)) {
        struct studentTag tmp = { .ID = 0 };    /* temporary struct to fill */
        if (sscanf (buf, "%d %19s %19s %f",     /* parse value from line */
                    &tmp.ID, tmp.name.first, tmp.name.last, &tmp.grade) == 4)
            std[n++] = tmp; /* assign temp struct to array on success */
        else    /* on error */
            fprintf (stderr, "error: line format - %s", buf);
    }
    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);

    for (size_t i = 0; i < n; i++)  /* output all stored student data */
        printf ("\nID: %d\nFirst: %s\nLast: %s\nGrade: %f\n",
                std[i].ID, std[i].name.first, std[i].name.last, std[i].grade);

    return 0;
}

注意:您总是通过检查 return来验证每个用户输入和每个值的解析,请参阅Henry Spencer 的 C 程序员的 10 条诫命 - 第 6 号“请注意...”

示例使用/输出

使用dat/studenttag.txt中的dat/studenttag.txt ,您将提供文件名作为第一个参数并接收:

$ ./bin/readstudenttag dat/studenttag.txt

ID: 1010
First: Jose
Last: Manalo
Grade: 3.500000

ID: 2003
First: Luisa
Last: Santos
Grade: 2.500000

ID: 1008
First: Andres
Last: Espinosa
Grade: 4.000000

仔细检查一下,如果您还有其他问题,请告诉我。

scanf用于从 stdin 读取输入流,也就是说,您可以使用的最近似的函数是 fscanf,类似于 scanf,但您可以指定要从中读取的文件:

#include <stdio.h>

typedef char String20[20];

struct nameTag {
    String20 first;
    String20 last;
};

struct studentTag {
    int ID;
    struct nameTag name;
    float grade;
};

int main()
{
   FILE* f;
   if(!(f = fopen("file.txt", "r"))) {
       return 1;
   }

   struct studentTag studentInfo; 

   while(fscanf(f, "%d %19s %19s %f", &studentInfo.ID, studentInfo.name.first, studentInfo.name.last, &studentInfo.grade) == 4)
        printf("%d %s %s %.1f\n", studentInfo.ID, studentInfo.name.first, studentInfo.name.last, studentInfo.grade);
}

现场样品

使用fscanf的返回来验证是否读取了正确数量的字段并将其用作停止参数。

请注意,对于 char 数组读取,指定要读取的字符串的长度很重要,例如,对于char[20]字符数组容器,您可以使用"%s19"说明符,以避免堆栈粉碎。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM