如果你要嘗試與fscanf
讀取變化的數據,那麼格式字符串將是你成功的關鍵(或失敗)。如果您需要收集的每一行都具有相同的格式,那麼您可以嘗試使用fscanf
來讀取數據(而您通過使用fgets
和sscanf
將讀取與解析&驗證解耦來獲得靈活性)。但是,只要您有辦法保留所需的線路,並跳過那些不需要的線路,則fscanf
即可用作工具。
在這種情況下,您的示例數據LYF_HKN在他的答案中做了很好的工作,以解決您需要的每一行中的所有信息。我想補充一個小的變體,並使用類似:
char *fmt = " %*[^\"]\"%[^,], %[^\"]\" %s %s %[YN] %lf %lf %lf %lf %lf";
其餘的是簡單的循環遍歷你的輸入文件中的行和索引你添加到您的結構中的學生。您需要捕獲return
的fscanf
,並將其與您需要的匹配計數與10
進行比較,以驗證您的每個值都已收到輸入。您還需要跳過標題行和其他不包含學生數據的行。只要fscanf
不會遇到EOF
設置流中的錯誤條件,您可以通過閱讀來完成此操作。然後,您只需檢查匹配計數(return
)與預期的10
以指示它是否是有效的學生數據行。
把這個放在一起,你可以這樣做以下:
#include <stdio.h>
/* constants for use in your code */
enum { YN = 2, DOB = 11, SS = 12, NM = 32 };
typedef struct {
char last[NM], first[NM], ss[SS], dob[DOB], yn[YN];
double qavg, lavg, ex1, ex2, fex;
} stnt;
int main (int argc, char **argv) {
stnt s[8] = {{ .first = "" }};
int cnv = 0, n = 0;
char *fmt = " %*[^\"]\"%[^,], %[^\"]\" %s %s %[YN] %lf %lf %lf %lf %lf";
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* read each line, if the match-count is 10 increment index */
while ((cnv=fscanf (fp, fmt, s[n].last, s[n].first, s[n].ss, s[n].dob, s[n].yn,
&s[n].qavg, &s[n].lavg, &s[n].ex1, &s[n].ex2, &s[n].fex)) != EOF)
if (cnv == 10) n++;
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (int i = 0; i < n; i++) /* output the student information */
printf ("\n student : %s %s\n ss number : %s\n D.O.B. : %s\n"
" yes/no : %s\n quiz avg : %.2lf\n lab avg : %.2lf\n"
" exam 1 : %.2lf\n exam 2 : %.2lf\n final : %.2lf\n",
s[i].first, s[i].last, s[i].ss, s[i].dob, s[i].yn, s[i].qavg,
s[i].lavg, s[i].ex1, s[i].ex2, s[i].fex);
return 0;
}
使用fgets
和sscanf
鑑於討論和關於使用fgets
和sscanf
的意見,除了fscanf
的例子之外,值得用一個簡短的例子來說明我使用的方法。該代碼大致相同,除了添加緩衝區buf
來保存用戶輸入線和基於fgets
返回的循環控制。除此之外,用sscanf
代替fscanf
只不過是代替sscanf
調用中的文件流的緩衝區。無論fscanf
和sscanf
返回匹配計數在於把基礎上,轉換指定地點成功轉換次數存在於格式字符串。
#include <stdio.h>
/* constants for use in your code */
enum { YN = 2, DOB = 11, SS = 12, NM = 32, MAXC = 256 };
typedef struct {
char last[NM], first[NM], ss[SS], dob[DOB], yn[YN];
double qavg, lavg, ex1, ex2, fex;
} stnt;
int main (int argc, char **argv) {
stnt s[8] = {{ .first = "" }};
int n = 0;
char buf[MAXC] = "",
*fmt = " %*[^\"]\"%[^,], %[^\"]\" %s %s %[YN] %lf %lf %lf %lf %lf";
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* read each line, if the match-count is 10 increment index */
while (fgets (buf, MAXC, fp))
if (sscanf (buf, fmt, s[n].last, s[n].first, s[n].ss, s[n].dob, s[n].yn,
&s[n].qavg, &s[n].lavg, &s[n].ex1, &s[n].ex2, &s[n].fex) == 10)
n++;
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (int i = 0; i < n; i++) /* output the student information */
printf ("\n student : %s %s\n ss number : %s\n D.O.B. : %s\n"
" yes/no : %s\n quiz avg : %.2lf\n lab avg : %.2lf\n"
" exam 1 : %.2lf\n exam 2 : %.2lf\n final : %.2lf\n",
s[i].first, s[i].last, s[i].ss, s[i].dob, s[i].yn, s[i].qavg,
s[i].lavg, s[i].ex1, s[i].ex2, s[i].fex);
return 0;
}
示例使用/輸出
使用您的數據(帶有),你最終會得到類似以下的輸出:
$ ./bin/rdstudents <dat/lab4text.txt
student : Christopher Jones
ss number : 162-74-2381
D.O.B. : 9/12/1995
yes/no : Y
quiz avg : 51.67
lab avg : 72.50
exam 1 : 77.00
exam 2 : 68.50
final : 61.00
student : Sarah Lee Abrahamson
ss number : 127-49-0853
D.O.B. : 11/5/1993
yes/no : N
quiz avg : 87.10
lab avg : 79.33
exam 1 : 64.25
exam 2 : 84.00
final : 72.50
student : Adreana Parker-Jones
ss number : 230-38-1234
D.O.B. : 3/1/1996
yes/no : Y
quiz avg : 75.23
lab avg : 81.04
exam 1 : 78.50
exam 2 : 80.00
final : 85.25
student : Joshua Ellis
ss number : 186-27-1372
D.O.B. : 7/31/1988
yes/no : Y
quiz avg : 85.23
lab avg : 94.90
exam 1 : 85.00
exam 2 : 92.00
final : 94.25
請張貼[MCVE] – purplepsycho
的'「%c」'格式(帶或不帶任何修飾符)不會跳過前導空格。嘗試在用於跳過管道字符的'「%* c」'之前在格式字符串中放置一個空格。同時檢查['scanf'](http://en.cppreference.com/w/c/io/fscanf)返回的結果。 –
閱讀每行並不容易,然後解析它們? –