詳解C語言fscanf函數(shù)讀取文件教程及源碼
fscanf 函數(shù)用于格式化讀入文件中數(shù)據(jù),可以大大提高讀取文件的效率。這次筆者將最近使用該函數(shù)的一些經(jīng)驗(yàn)記錄下來。
第一部分:問題和結(jié)論
fscanf 函數(shù)的原型是:
int fscanf(FILE* stream, const char* format, [argument...]);
fscanf 函數(shù)與 scanf 函數(shù)用法類似,只不過前者用于讀取文件流的數(shù)據(jù)而已。至于 fscanf 的基礎(chǔ)用法我就不贅述了,網(wǎng)上的文章很多。簡單提及一下要點(diǎn):
- 1. format str:如%d, %f, %c, %s等,分別表示讀入一個整數(shù),浮點(diǎn)數(shù),字符,字符串。還可以加上控制,如%ld,表示讀入一個長整型數(shù),%20s表示最多讀入20個字符。
- 2. 返回值:在沒有出錯的情況下,fscanf 返回正確匹配和賦值的域的個數(shù);如果出錯,則返回EOF。
fscanf 的難點(diǎn)在于以下幾點(diǎn):
- 1. 對空白符的處理(空格、制表符、換行符);
- 2. *的用法;
- 3. [] 以及 [^] 的用法;
- 4. EOF 的處理;
對于上述問題,網(wǎng)上的文章都語焉不詳,所以筆者自己寫了點(diǎn)實(shí)驗(yàn)代碼進(jìn)行驗(yàn)證,應(yīng)該是比較詳細(xì)的小結(jié)了。
先把結(jié)論羅列在下面,具體的實(shí)驗(yàn)代碼放在文章最后:
- 1. 在空白符這個意義上來講,fscanf 對空格、制表符、換行符是一視同仁的,不加區(qū)分的;%s會跳過前面的空白符,但是不會跳過后面的空白符;%c不會跳過空白符。
- 2. *表示讀取一個域,但是不賦值給變量。
- 3. []表示只讀取中括號內(nèi)的字符,[]表示不讀取中括號內(nèi)的字符,值得注意的是%[]s將不會跳過前面的空白符。
- 4. 如果還沒有任何一個域匹配成功或者任何一個匹配失敗發(fā)生之前,就達(dá)到了文件流末尾,就算出錯;或者讀取文件流出錯。這兩種情況下,fscanf 返回EOF。
第二部分:實(shí)驗(yàn)代碼
為了驗(yàn)證上面提出的一些問題,筆者動手寫了下面的實(shí)驗(yàn)代碼進(jìn)行驗(yàn)證分析,代碼共分為六個部分,注意每個部分所使用的文件內(nèi)容是不一樣的:
- 1. fscanf 對空格的處理;
- 2. fscanf 對制表符的處理;
- 3. fscanf 對換行符的處理;
- 4. 當(dāng)空格、制表符以及換行符混雜時fscanf的處理;
- 5. []符號在format str中的應(yīng)用;
- 6. 出錯的情況。
實(shí)驗(yàn)代碼:
(注意為了清晰無誤地表示出不同的空白符,用 <\b>代表空格,<\t>表示制表符,<\n>表示換行符。)
#include <stdio.h>
#include <errno.h>
void fscanfTest(FILE* fp) {
char c1, c2, s1[100], s2[100];
int d;
// 第一部分:fscanf對空格的處理
printf("the content of file is:\n");
printf("hello<\\b>world<\\b><\\b>666lucky<\\n>");
printf("\n\n");
// %s不會跳過后面的空格
fscanf(fp, "%s", s1);
printf("%s!\n", s1); // hello!
// %s會跳過前面的一個空格
rewind(fp); // 將光標(biāo)移回文件開頭
fscanf(fp, "%s%s", s2, s1);
printf("%s! %s!\n", s2, s1); // hello! world!
// %*s會從文件流中讀入,但是不會將值賦予變量(*的作用)
rewind(fp);
fscanf(fp, "%*s%s", s1);
printf("%s!\n", s1); // world!
// %s會跳過前面的多個空格
rewind(fp);
fscanf(fp, "%*s%s%s", s2, s1);
printf("%s! %s!\n", s2, s1); // world! 666lucky!
// %c不會跳過空格
rewind(fp);
fscanf(fp, "%*s%c", &c1);
printf("%c!\n", c1); // " !"
// format str中的一個空格表示如果文件流接下來有連續(xù)空格,都跳過
rewind(fp);
fscanf(fp, "%*s%*s %c", &c1);
printf("%c!\n", c1); // "6!"
rewind(fp);
fscanf(fp, "%*s%*s%*d%c", &c1);
printf("%c!\n", c1); // "l!"
rewind(fp);
fscanf(fp, "%*s%*s%*d %c", &c2); // 注意這里format str中的空格沒起作用,是因?yàn)?66和lucky之間沒有空白符
printf("%c!\n", c2); // "l!"
rewind(fp);
fscanf(fp, "%*s%*s%*d%s", s1);
printf("%s!\n", s1); // "lucky!"
rewind(fp);
fscanf(fp, "%*s%*s%*d %s", s2);
printf("%s!\n", s2); // "lucky!"
// format str中的多個連續(xù)空格和一個空格的效果是一樣的
rewind(fp);
fscanf(fp, "%*s %c", &c1);
printf("%c!\n", c1); // "w!"
rewind(fp);
fscanf(fp, "%*s %c", &c2);
printf("%c!\n", c2); // "w!"
// 第二部分:fscanf對制表符的處理
printf("the content of file is:\n");
printf("hello<\\t>world<\\t><\\t>666lucky<\\n>");
printf("\n\n");
// %s不會跳過后面的制表符
fscanf(fp, "%s", s1);
printf("%s!\n", s1); // hello!
// %s會跳過前面的一個制表符
rewind(fp);
fscanf(fp, "%s%s", s2, s1);
printf("%s! %s!\n", s2, s1); // hello! world!
// %s會跳過前面的多個制表符
rewind(fp);
fscanf(fp, "%*s%s%s", s2, s1);
printf("%s! %s!\n", s2, s1); // world! 666lucky!
// %c不會跳過制表符
rewind(fp);
fscanf(fp, "%*s%c", &c1);
printf("%c!\n", c1); // "<\\t>!"
// format str中的一個制表符表示如果文件流接下來有連續(xù)制表符,都跳過
rewind(fp);
fscanf(fp, "%*s%*s\t%c", &c1);
printf("%c!\n", c1); // "6!"
rewind(fp);
fscanf(fp, "%*s%*s%*d%c", &c1);
printf("%c!\n", c1); // "l!"
rewind(fp);
fscanf(fp, "%*s%*s%*d\t%c", &c2);
printf("%c!\n", c2); // "l!"
rewind(fp);
fscanf(fp, "%*s%*s%*d%s", s1);
printf("%s!\n", s1); // "lucky!"
rewind(fp);
fscanf(fp, "%*s%*s%*d\t%s", s2);
printf("%s!\n", s2); // "lucky!"
// format str中的多個連續(xù)制表符和一個制表符的效果是一樣的
rewind(fp);
fscanf(fp, "%*s\t%c", &c1);
printf("%c!\n", c1); // "w!"
rewind(fp);
fscanf(fp, "%*s\t\t%c", &c2);
printf("%c!\n", c2); // "w!"
// 第三部分:fscanf對換行符的處理
printf("the content of file is:\n");
printf("hello<\\n>world<\\n><\\n>666lucky<\\n>");
printf("\n\n");
// %s不會跳過后面的換行符
fscanf(fp, "%s", s1);
printf("%s!\n", s1); // hello!
// %s會跳過前面的一個換行符
rewind(fp);
fscanf(fp, "%s%s", s2, s1);
printf("%s! %s!\n", s2, s1); // hello! world!
// %s會跳過前面的多個換行符
rewind(fp);
fscanf(fp, "%*s%s%s", s2, s1);
printf("%s! %s!\n", s2, s1); // world! 666lucky!
// %c不會跳過換行符
rewind(fp);
fscanf(fp, "%*s%c", &c1);
printf("%c!\n", c1); // "<\\n>!"
// format str中的一個換行符表示如果文件流接下來有連續(xù)換行符,都跳過
rewind(fp);
fscanf(fp, "%*s%*s\n%c", &c1);
printf("%c!\n", c1); // "6!"
rewind(fp);
fscanf(fp, "%*s%*s%*d%c", &c1);
printf("%c!\n", c1); // "l!"
rewind(fp);
fscanf(fp, "%*s%*s%*d\n%c", &c2);
printf("%c!\n", c2); // "l!"
rewind(fp);
fscanf(fp, "%*s%*s%*d%s", s1);
printf("%s!\n", s1); // "lucky!"
rewind(fp);
fscanf(fp, "%*s%*s%*d\n%s", s2);
printf("%s!\n", s2); // "lucky!"
// format str中的多個連續(xù)換行符和一個換行符的效果是一樣的
rewind(fp);
fscanf(fp, "%*s\n%c", &c1);
printf("%c!\n", c1); // "w!"
rewind(fp);
fscanf(fp, "%*s\n\n%c", &c2);
printf("%c!\n", c2); // "w!"
// 第四部分:當(dāng)空格、制表符以及換行符混雜時fscanf的處理
printf("the content of file is:\n");
printf("hello<\\b><\\t><\\n>world<\\t><\\b><\\n>666lucky<\\n>");
printf("\n\n");
// %s會跳過連在一起的空格、制表符和換行符
fscanf(fp, "%s%s", s2, s1);
printf("%s! %s!\n", s2, s1); // hello! world!
// 當(dāng)作為空白符時,format str中的空格、制表符以及換行符是一樣的,可以相互替代!
rewind(fp);
fscanf(fp, "%*s %c", &c1);
printf("%c!\n", c1); // "w!"
rewind(fp);
fscanf(fp, "%*s\t%c", &c2);
printf("%c!\n", c2); // "w!"
rewind(fp);
fscanf(fp, "%*s\n%c", &c1);
printf("%c!\n", c1); // "w!"
// 第五部分:[]符號在format str中的應(yīng)用
printf("the content of file is:\n");
printf("hello<\\b><\\t><\\n>world<\\b><\\t>666lucky<\\n>");
printf("\n\n");
// [el]表示只讀取'e'或者'l'這個字符,[0-9]表示只讀取0-9這10個數(shù)字字符
// %[]之后的域都不起作用了,不會讀取文件流。
// test#1: %c%[]s可以正常工作
// output#1: h! ell!
errno = 0;
d = fscanf(fp, "%c%[el]s", &c1, s1);
if (d == 2) printf("%c! %s!\n", c1, s1);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
// test#2: %[]s后面的%c沒有正常讀取
// output#2: d = 2
errno = 0;
rewind(fp);
d = fscanf(fp, "%c%[el]s%c", &c2, s2, &c1);
if (d == 3) printf("%c! %s! %c!\n", c2, s2, c1);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
// test#3: %[]s后面的%s沒有正常讀取
// output#3: d = 2
errno = 0;
rewind(fp);
d = fscanf(fp, "%c%[el]s%s", &c1, s1, s2);
if (d == 3) printf("%c! %s! %s!\n", c1, s1, s2);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
// test#4: 再次運(yùn)行fscanf函數(shù)就可以繼續(xù)讀取文件流
// output#4: o! world!
errno = 0;
d = fscanf(fp, "%c%s", &c2, s2);
if (d == 2) printf("%c! %s!\n", c2, s2);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
// [^el]表示不讀取'e'也不讀取'l'這個字符,[^0-9]表示不讀取0-9的數(shù)字字符
// %[^]之后的域都不起作用了,不會讀取文件流。
// test#5: %c%[^]s可以正常工作,注意下面的%[^w]s這個域讀取了空格、制表符以及換行符。
// output#5: h! ello<\\b><\\t><\\n>!
errno = 0;
rewind(fp);
d = fscanf(fp, "%c%[^w]s", &c1, s1);
if (d == 2) printf("%c! %s!\n", c1, s1);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
// test#6: %[^]s后面的%s沒有正常讀取
// output#6: d = 2
errno = 0;
rewind(fp);
d = fscanf(fp, "%c%[^w]s%s", &c2, s2, s1);
if (d == 3) printf("%c! %s! %s!\n", c2, s2, s1);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
// test#7: 再次運(yùn)行fscanf函數(shù)就可以繼續(xù)讀取文件流
// output#7: w! orld!
errno = 0;
d = fscanf(fp, "%c%s", &c1, s1);
if (d == 2) printf("%c! %s!\n", c1, s1);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
// test#8: %[^\n]s可以一直讀取到行末尾,哪怕遇到空格或者制表符。
// output#8: h! ello<\\b><\\t>!
errno = 0;
rewind(fp);
d = fscanf(fp, "%c%[^\n]s", &c2, s2);
if (d == 2) printf("%c! %s!\n", c2, s2);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
// test#9: %[^ ]s不會讀取空格,但是會讀取制表符和換行符
// output#9: <\\t><\\n>world!
errno = 0;
rewind(fp);
d = fscanf(fp, "%*s%*c%[^ ]s", s1);
if (d == 1) printf("%s!\n", s1);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
// test#10: %[^\t]s不會讀取制表符,但是會讀取空格和換行符
// output#10: <\\n>world<\\b>!
errno = 0;
rewind(fp);
d = fscanf(fp, "%*s%*c%*c%[^\t]s", s2);
if (d == 1) printf("%s!\n", s2);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
// test#11: %[^]s不會跳過前面的空白符
// output#11: <\\b><\\t><\\n>wo!
errno = 0;
rewind(fp);
d = fscanf(fp, "%*s%[^r]s", s1);
if (d == 1) printf("%s!\n", s1);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
// 第六部分:出錯的情況
// 從第五部分 test#2 以及 test#3 的例子中可以看出,fscanf的返回值表示能夠正確賦值的域的個數(shù)。如果出錯,fscanf返回EOF。
// 怎樣才算出錯?如果還沒有任何一個域匹配成功或者任何一個匹配失敗發(fā)生之前,就達(dá)到了文件流末尾,就算出錯;或者讀取文件流出錯。就這兩種情況。
// 即使所有域都不匹配,但只要沒到達(dá)文件流末尾并且讀取文件流過程中沒有發(fā)生錯誤,就不算出錯,errno就是0。此時,fscanf返回0。
printf("the content of file is:\n");
printf("hello");
printf("\n\n");
// test#1: 此時的%c發(fā)生匹配失敗,所以返回值為0。
// output#1: d = 0
errno = 0;
d = fscanf(fp, "%*s%c", &c1);
if (d == 1) printf("%c!\n", c1);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
// test#2: 繼續(xù)讀取,已經(jīng)到達(dá)文件流末尾,返回EOF。
// output#2: d = -1
errno = 0;
d = fscanf(fp, "%c", &c2);
if (d == 1) printf("%c!\n", c2);
else {
printf("d = %d\n", d);
if (errno != 0) perror("fscanf");
else printf("Error: no matching characters!\n");
}
}
int main(int argc, char* argv[]) {
FILE *fp;
if (argc < 2) {
printf("Usage: %s <filename>\n", argv[0]);
return 1;
}
if ((fp = fopen(argv[1], "r")) == NULL) {
printf("Error: cannot open file\n");
return 1;
}
fscanfTest(fp);
fclose(fp);
return 0;
}
以上就是詳解C語言fscanf函數(shù)讀取文件示例教程的詳細(xì)內(nèi)容,更多關(guān)于C語言fscanf讀取文件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
在Ubuntu中安裝VSCode并配置C/C++開發(fā)環(huán)境的方法步驟
這篇文章主要介紹了在Ubuntu中安裝VSCode并配置C/C++開發(fā)環(huán)境的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
C++實(shí)現(xiàn)string存取二進(jìn)制數(shù)據(jù)的方法
這篇文章主要介紹了C++實(shí)現(xiàn)string存取二進(jìn)制數(shù)據(jù)的方法,針對STL中string的用法進(jìn)行了較為詳細(xì)的分析,需要的朋友可以參考下2014-10-10
Matlab實(shí)現(xiàn)帶豎線散點(diǎn)的核密度圖的繪制
核密度估計(jì)是用于估計(jì)隨機(jī)變量概率密度函數(shù)的一種非參數(shù)方法。核密度圖不失為一種用來觀察連續(xù)型變量分布的有效方法。本文將用Matlab實(shí)現(xiàn)帶豎線散點(diǎn)的核密度圖的繪制,感興趣的可以了解一下2022-08-08
C++ 基礎(chǔ)教程之虛函數(shù)實(shí)例代碼詳解
虛函數(shù)在 c++ 的繼承體系中是一個非常重要概念,讓我們可以在子類中復(fù)寫父類的方法。這篇文章主要介紹了C++ 基礎(chǔ)教程之虛函數(shù)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2020-02-02
詳解C語言中symlink()函數(shù)和readlink()函數(shù)的使用
這篇文章主要介紹了詳解C語言中symlink()函數(shù)和readlink()函數(shù)的使用,是C語言入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-09-09
Qt+GDAL庫實(shí)現(xiàn)制作經(jīng)緯度坐標(biāo)轉(zhuǎn)換工具
這篇文章主要為大家詳細(xì)介紹了如何利用Qt和GDAL庫實(shí)現(xiàn)制作經(jīng)緯度坐標(biāo)轉(zhuǎn)換工具,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-04-04

