服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - 详解C语言fscanf函数读取文件教程及源码

详解C语言fscanf函数读取文件教程及源码

2022-09-22 16:12hxj7 C/C++

这篇文章主要为大家介绍了详解C语言算法fscanf读取文件示例教程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步

fscanf 函数用于格式化读入文件中数据,可以大大提高读取文件的效率。这次笔者将最近使用该函数的一些经验记录下来。

第一部分:问题和结论

fscanf 函数的原型是:

?
1
int fscanf(FILE* stream, const char* format, [argument...]);

fscanf 函数与 scanf 函数用法类似,只不过前者用于读取文件流的数据而已。至于 fscanf 的基础用法我就不赘述了,网上的文章很多。简单提及一下要点:

  • 1. format str:如%d, %f, %c, %s等,分别表示读入一个整数,浮点数,字符,字符串。还可以加上控制,如%ld,表示读入一个长整型数,%20s表示最多读入20个字符。
  • 2. 返回值:在没有出错的情况下,fscanf 返回正确匹配和赋值的域的个数;如果出错,则返回EOF。

fscanf 的难点在于以下几点:

  • 1. 对空白符的处理(空格、制表符、换行符);
  • 2. *的用法;
  • 3. [] 以及 [^] 的用法;
  • 4. EOF 的处理;

对于上述问题,网上的文章都语焉不详,所以笔者自己写了点实验代码进行验证,应该是比较详细的小结了。
先把结论罗列在下面,具体的实验代码放在文章最后:

  • 1. 在空白符这个意义上来讲,fscanf 对空格、制表符、换行符是一视同仁的,不加区分的;%s会跳过前面的空白符,但是不会跳过后面的空白符;%c不会跳过空白符。
  • 2. *表示读取一个域,但是不赋值给变量。
  • 3. []表示只读取中括号内的字符,[]表示不读取中括号内的字符,值得注意的是%[]s将不会跳过前面的空白符。
  • 4. 如果还没有任何一个域匹配成功或者任何一个匹配失败发生之前,就达到了文件流末尾,就算出错;或者读取文件流出错。这两种情况下,fscanf 返回EOF。

第二部分:实验代码

为了验证上面提出的一些问题,笔者动手写了下面的实验代码进行验证分析,代码共分为六个部分,注意每个部分所使用的文件内容是不一样的:

  • 1. fscanf 对空格的处理;
  • 2. fscanf 对制表符的处理;
  • 3. fscanf 对换行符的处理;
  • 4. 当空格、制表符以及换行符混杂时fscanf的处理;
  • 5. []符号在format str中的应用;
  • 6. 出错的情况。

实验代码:

(注意为了清晰无误地表示出不同的空白符,用 <\b>代表空格,<\t>表示制表符,<\n>表示换行符。)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#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);   // 将光标移回文件开头
  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中的一个空格表示如果文件流接下来有连续空格,都跳过
  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中的空格没起作用,是因为666和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中的多个连续空格和一个空格的效果是一样的
  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中的一个制表符表示如果文件流接下来有连续制表符,都跳过
  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中的多个连续制表符和一个制表符的效果是一样的
  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中的一个换行符表示如果文件流接下来有连续换行符,都跳过
  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中的多个连续换行符和一个换行符的效果是一样的
  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!"
  // 第四部分:当空格、制表符以及换行符混杂时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!
  // 当作为空白符时,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中的应用
  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个数字字符
  // %[]之后的域都不起作用了,不会读取文件流。
  // 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: 再次运行fscanf函数就可以继续读取文件流
  // 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的数字字符
  // %[^]之后的域都不起作用了,不会读取文件流。
  // 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: 再次运行fscanf函数就可以继续读取文件流
  // 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的返回值表示能够正确赋值的域的个数。如果出错,fscanf返回EOF。
  // 怎样才算出错?如果还没有任何一个域匹配成功或者任何一个匹配失败发生之前,就达到了文件流末尾,就算出错;或者读取文件流出错。就这两种情况。
  // 即使所有域都不匹配,但只要没到达文件流末尾并且读取文件流过程中没有发生错误,就不算出错,errno就是0。此时,fscanf返回0。
  printf("the content of file is:\n");
  printf("hello");
  printf("\n\n");
  // test#1: 此时的%c发生匹配失败,所以返回值为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: 继续读取,已经到达文件流末尾,返回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函数读取文件示例教程的详细内容,更多关于C语言fscanf读取文件的资料请关注服务器之家其它相关文章!

原文链接:https://blog.csdn.net/biocity/article/details/91900105

延伸 · 阅读

精彩推荐