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