[英]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.