C fscanf 函数
最后修改日期:2025 年 4 月 6 日
格式化文件输入对于在 C 编程中读取结构化数据至关重要。fscanf
函数提供了强大的功能,可根据指定的模式解析文件。本教程将深入讲解 fscanf
,涵盖其语法、格式说明符和实际应用。您将学习如何从文件中读取各种数据类型,同时了解重要的安全注意事项。
什么是 fscanf?
fscanf
函数从文件流中读取格式化输入,类似于 scanf
,但用于文件。它将文件指针、格式字符串和变量地址作为参数。该函数返回成功匹配的项数,或在失败时返回 EOF
。始终检查此返回值以进行错误处理。与 scanf
不同,它更安全,因为它不直接从 stdin 读取。
基本的 fscanf 语法
函数原型是 int fscanf(FILE *stream, const char *format, ...)
。第一个参数是通过 fopen
获取的文件指针。格式字符串包含转换说明符,例如整数的 %d
。附加参数是指向将存储读取值的变量的指针。对于大多数说明符,该函数默认在遇到空格时停止读取。
从文件中读取整数
本示例演示了如何使用 fscanf
从文件读取整数值。
#include <stdio.h> int main() { FILE *fp = fopen("numbers.txt", "r"); if (fp == NULL) { perror("Error opening file"); return 1; } int num1, num2, num3; int items_read = fscanf(fp, "%d %d %d", &num1, &num2, &num3); if (items_read != 3) { printf("Only read %d values\n", items_read); } else { printf("Numbers: %d, %d, %d\n", num1, num2, num3); } fclose(fp); return 0; }
此代码打开 "numbers.txt" 并尝试读取三个整数。格式字符串 "%d %d %d"
指定了三个由空格分隔的整数值。我们存储返回值以验证成功读取的项数。始终检查此值以妥善处理部分读取或读取失败的情况。文件在最后被正确关闭。
读取混合数据类型
fscanf
可以在一次读取操作中处理不同的数据类型。
#include <stdio.h> int main() { FILE *fp = fopen("data.txt", "r"); if (fp == NULL) { perror("File open error"); return 1; } char name[50]; int age; float height; int result = fscanf(fp, "%49s %d %f", name, &age, &height); if (result == 3) { printf("Name: %s\nAge: %d\nHeight: %.2f\n", name, age, height); } else { printf("Error reading data (read %d items)\n", result); } fclose(fp); return 0; }
在这里,我们从文件中读取一个字符串、一个整数和一个浮点数。请注意 %49s
格式说明符,它限制了字符串输入以防止缓冲区溢出。通过 &
运算符(数组除外)传递变量的地址。返回值检查确保在处理所有三个项之前已成功读取它们。这展示了 fscanf
处理不同数据类型的通用性。
逐行读取文件
本示例演示了如何使用 fscanf
逐行处理文件。
#include <stdio.h> int main() { FILE *fp = fopen("records.txt", "r"); if (fp == NULL) { perror("Cannot open file"); return 1; } char line[256]; while (fscanf(fp, "%255[^\n]\n", line) != EOF) { printf("Line: %s\n", line); } fclose(fp); return 0; }
格式字符串 "%255[^\n]\n"
读取最多 255 个字符,直到遇到换行符,然后消耗该换行符。while
循环一直持续到返回 EOF
。请注意,缓冲区大小(256)比最大读取数(255)大一,以便为 null 终止符留出空间。这种方法对于逐条记录处理结构化文本文件很有用。
使用字段宽度读取
使用字段宽度可防止读取字符串时发生缓冲区溢出。
#include <stdio.h> int main() { FILE *fp = fopen("users.txt", "r"); if (fp == NULL) { perror("File open failed"); return 1; } char username[21]; // 20 chars + null terminator int score; while (fscanf(fp, "%20s %d", username, &score) == 2) { printf("User: %s, Score: %d\n", username, score); } fclose(fp); return 0; }
%20s
格式说明符确保不会将超过 20 个字符读入 username
数组(该数组有 21 个元素)。只要成功读取用户名和分数,循环就会继续。这是从不受信任的文件读取字符串时的关键安全措施。始终将字段宽度设置为缓冲区大小减一(为 null 终止符留出空间)。
高级模式匹配
fscanf
可以使用高级格式说明符解析复杂模式。
#include <stdio.h> int main() { FILE *fp = fopen("log.txt", "r"); if (fp == NULL) { perror("Error opening log file"); return 1; } char month[4]; int day, year; char message[100]; while (fscanf(fp, "%3s %d, %d: %99[^\n]", month, &day, &year, message) == 4) { printf("[%d-%s-%d] %s\n", day, month, year, message); } fclose(fp); return 0; }
此示例解析一个日志文件,其中包含类似 "Jun 15, 2023: System started" 的条目。格式字符串的分解如下:%3s
用于 3 个字符的月份,%d, %d
用于日期和年份,%99[^\n]
用于该行的其余部分。方括号 [^\n]
创建了一个扫描集,该扫描集匹配除换行符之外的所有字符。这展示了 fscanf
用于结构化文本解析的强大模式匹配功能。
使用 fscanf 的最佳实践
- 检查返回值:始终验证读取的项数以检测错误。
- 使用字段宽度:通过指定字符串的最大长度来防止缓冲区溢出。
- 验证输入:检查读取的值是否在预期范围内。
- 考虑替代方案:对于复杂的解析,使用
fgets
和sscanf
可能更安全。 - 处理空格:请注意,对于大多数转换,
fscanf
会跳过前导空格。
来源
本教程深入探讨了 fscanf
函数,从基本用法到高级模式匹配。当谨慎使用并进行适当的错误检查和字段宽度规范时,它是从文件读取格式化数据的强大工具。请记住,始终验证输入,并在关键应用程序中考虑更安全的选择。
作者
列表 C 标准库。