ZetCode

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 从文件读取整数值。

read_integers.c
#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 可以在一次读取操作中处理不同的数据类型。

mixed_data.c
#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 逐行处理文件。

read_lines.c
#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 终止符留出空间。这种方法对于逐条记录处理结构化文本文件很有用。

使用字段宽度读取

使用字段宽度可防止读取字符串时发生缓冲区溢出。

field_width.c
#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 可以使用高级格式说明符解析复杂模式。

pattern_match.c
#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 的最佳实践

来源

C fscanf 文档

本教程深入探讨了 fscanf 函数,从基本用法到高级模式匹配。当谨慎使用并进行适当的错误检查和字段宽度规范时,它是从文件读取格式化数据的强大工具。请记住,始终验证输入,并在关键应用程序中考虑更安全的选择。

作者

我的名字是 Jan Bodnar,我是一名敬业的程序员,对编码充满热情。自 2007 年以来,我通过 1,400 多篇文章和 8 本电子书分享了我的专业知识。凭借十多年的教学经验,我致力于使编程变得易于理解且引人入胜。

列表 C 标准库