fscanf函数如何正确读取文件数据?

99ANYc3cd6
预计阅读时长 20 分钟
位置: 首页 C语言 正文

什么是 fscanf

fscanf 是 C 标准库中的一个函数,全称为 "file scan formatted",它的作用是从一个文件中按照指定的格式读取(扫描)数据,并将这些数据存储到程序提供的变量中。

c语言fscanf函数的用法
(图片来源网络,侵删)

你可以把它理解为 scanf 函数的文件版本。scanf 从标准输入(通常是键盘)读取数据,而 fscanf 从一个文件指针(FILE*)指向的文件中读取数据。

函数原型

#include <stdio.h>
int fscanf(FILE *stream, const char *format, ...);

参数解析:

  1. stream: FILE* 类型的指针,指向你要读取的文件,这个指针通常是由 fopen() 函数打开文件后返回的。
  2. format: 一个字符串,包含了格式说明符,用来告诉 fscanf 如何解释文件中的数据,这与 printfscanf 中的格式字符串类似。
  3. (省略号) 表示可变参数列表,你需要提供与 format 字符串中格式说明符数量和类型相匹配的变量的地址。

返回值:

  • 成功时,fscanf 返回成功匹配并赋值的字段的数量,这个数量不包括被跳过的空白字符。
  • 如果在读取任何数据之前就到达了文件末尾,则返回 EOF (End of File)。
  • 如果发生读取错误(文件损坏),也返回 EOF
  • 如果在匹配过程中发生错误(期望一个数字但遇到了字母),则返回已成功赋值的字段的数量。

格式说明符

fscanf 的强大之处在于其格式化能力,常用的格式说明符如下:

c语言fscanf函数的用法
(图片来源网络,侵删)
格式说明符 对应的C数据类型 描述
%d int 读取一个十进制整数。
%f float 读取一个浮点数。
%lf double 读取一个双精度浮点数。注意:在 fscanf 中,读取 double 必须使用 %lf,这是初学者最容易犯的错误之一。
%c char 读取一个单个字符。
%s char* (字符数组) 读取一个字符串,直到遇到空白字符(空格、制表符、换行符)。
%[...] char* (字符数组) 读取一个字符集合。%[a-z] 读取所有小写字母。%[^,] 读取直到遇到逗号为止的所有字符。
%n int* 不读取字符,而是将到目前为止已读取的字符数存入指定的 int 变量中。

基本用法示例

示例 1:读取整数

假设我们有一个名为 numbers.txt 的文件,内容如下:

10
20
30
40

代码:

#include <stdio.h>
int main() {
    FILE *fp;
    int num1, num2, num3, num4;
    // 以只读模式打开文件
    fp = fopen("numbers.txt", "r");
    if (fp == NULL) {
        perror("无法打开文件");
        return 1;
    }
    // 从文件中读取4个整数
    int count = fscanf(fp, "%d %d %d %d", &num1, &num2, &num3, &num4);
    if (count == 4) {
        printf("成功读取了 %d 个整数,\n", count);
        printf("num1 = %d, num2 = %d, num3 = %d, num4 = %d\n", num1, num2, num3, num4);
    } else {
        printf("读取失败或文件内容不匹配,成功读取了 %d 个整数,\n", count);
    }
    // 关闭文件
    fclose(fp);
    return 0;
}

输出:

成功读取了 4 个整数。
num1 = 10, num2 = 20, num3 = 30, num4 = 40

示例 2:读取混合数据(字符串和浮点数)

假设我们有一个名为 data.txt 的文件,内容如下:

c语言fscanf函数的用法
(图片来源网络,侵删)
Alice 95.5
Bob 88.0
Charlie 76.5

代码:

#include <stdio.h>
#include <string.h> // 为了使用 strlen
int main() {
    FILE *fp;
    char name[50];
    double score;
    fp = fopen("data.txt", "r");
    if (fp == NULL) {
        perror("无法打开文件");
        return 1;
    }
    printf("正在读取学生数据...\n");
    // 循环读取,直到文件末尾
    // fscanf 返回成功读取的字段数,对于 "name score" 这个格式,成功应该是2
    while (fscanf(fp, "%s %lf", name, &score) == 2) {
        printf("姓名: %-10s 分数: %.2f\n", name, score);
    }
    fclose(fp);
    return 0;
}

输出:

正在读取学生数据...
姓名: Alice      分数: 95.50
姓名: Bob        分数: 88.00
姓名: Charlie    分数: 76.50

示例 3:处理文件末尾

使用 feof() 函数可以更准确地判断是否到达文件末尾。

#include <stdio.h>
int main() {
    FILE *fp;
    int number;
    fp = fopen("numbers.txt", "r");
    if (fp == NULL) {
        perror("无法打开文件");
        return 1;
    }
    printf("开始读取文件内容...\n");
    while (1) {
        int items_read = fscanf(fp, "%d", &number);
        if (items_read == EOF) {
            // 检查是否是因为到达文件末尾而退出
            if (feof(fp)) {
                printf("\n已到达文件末尾,\n");
            } else {
                // 或者是因为发生错误
                printf("\n读取文件时发生错误,\n");
            }
            break; // 退出循环
        } else if (items_read == 1) {
            // 成功读取一个整数
            printf("读取到数字: %d\n", number);
        } else {
            // 格式不匹配等意外情况
            printf("格式不匹配,停止读取,\n");
            break;
        }
    }
    fclose(fp);
    return 0;
}

重要注意事项和最佳实践

  1. 检查 fopen 的返回值:在尝试使用 FILE* 指针之前,必须检查它是否为 NULL,如果文件打开失败(文件不存在或没有权限),后续的 fscanf 操作会导致未定义行为(通常是程序崩溃)。

  2. double 必须用 %lf:这是最常见的错误,在 scanffscanf 中,读取 double 类型变量时,格式说明符必须是 %lf,如果你写成 %f,行为是未定义的,几乎肯定会导致数据读取错误。

  3. 处理返回值:不要假设 fscanf 总是能成功读取你期望的数据,总是检查它的返回值,以确定实际读取了多少个字段,这对于健壮的程序至关重要。

  4. 关闭文件:使用完文件后,务必调用 fclose(fp) 来关闭文件,这会释放系统资源,并将缓冲区中的数据写入磁盘(如果是以写模式打开),如果程序异常退出,没有关闭的文件可能会导致数据丢失。

  5. %s 的陷阱%s 遇到空白字符就会停止,并且它不会在读取的字符串末尾自动添加 '\0'fscanf 会为你做这件事,因为它会根据读取的字符数量来填充字符数组,如果你用一个固定大小的数组去读取一个可能很长的字符串,有缓冲区溢出的风险,使用 fgets 通常比 fscanf 来读取一行字符串更安全。

  6. %c 会读取空白字符:与 %s 不同,%c 会读取文件中的每一个字符,包括空格、制表符和换行符,如果你想在 fscanf 中跳过这些空白字符,可以在 %c 前面加一个空格,fscanf(fp, " %c", &c),这个空格会告诉 fscanf 先跳过所有连续的空白字符,然后再读取一个非空白字符。

fscanf vs. fgets + sscanf

在读取结构化文本文件时,一个更常用、更稳健的模式是:

  1. 使用 fgets 一次读取一行文本到一个字符串缓冲区中。
  2. 使用 sscanf (从字符串扫描) 从这个缓冲区中解析出所需的数据。

为什么这样做更好?

  • 健壮性fgets 读取一行,可以处理每行长度不一致的情况,而 fscanf 如果某行格式不对,可能会“卡住”在那一行,影响后续所有行的读取。
  • 可控性:你可以先检查整行内容,再用 sscanf 解析,逻辑更清晰。

示例: 读取 data.txt

#include <stdio.h>
int main() {
    FILE *fp;
    char line[100]; // 缓冲区,用于存放一行文本
    char name[50];
    double score;
    fp = fopen("data.txt", "r");
    if (fp == NULL) {
        perror("无法打开文件");
        return 1;
    }
    while (fgets(line, sizeof(line), fp) != NULL) {
        // 尝试从字符串 line 中解析数据
        if (sscanf(line, "%s %lf", name, &score) == 2) {
            printf("姓名: %-10s 分数: %.2f\n", name, score);
        }
    }
    fclose(fp);
    return 0;
}

这个版本和前面的 fscanf 版本功能相同,但通常被认为更易于维护和调试。

fscanf 是一个强大的工具,用于从文件中按格式读取数据,掌握它的关键在于:

  • 正确使用:始终检查 fopen 返回值和 fscanf 的返回值。
  • 理解格式:特别是 %lf 用于 double
  • 知道替代方案:对于复杂的文本解析,fgets + sscanf 往往是更可靠的选择。

希望这份详细的讲解能帮助你完全掌握 fscanf 的用法!

-- 展开阅读全文 --
头像
织梦CMS标签如何正确调用?
« 上一篇 今天
dede仿自动切换图代码如何实现?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]