C 库函数 – fgets()(详细教程)

C 库函数 – fgets() 的全面解析:从入门到实战

在 C 语言编程中,读取用户输入或文件内容是几乎每个程序都会涉及的操作。虽然 scanf() 是初学者最常接触的输入函数,但它的局限性也显而易见。当你需要读取包含空格的字符串,或者从文件中逐行读取内容时,scanf() 就显得力不从心了。

这时,fgets() 函数便成为你手中强有力的工具。作为 C 标准库中用于安全读取字符串的核心函数之一,fgets() 不仅功能强大,而且在安全性上远胜于 gets()(已被废弃)。掌握它,意味着你离写出更健壮、更安全的 C 程序又近了一步。

本文将带你从零开始,深入理解 fgets() 的工作原理、使用场景与常见陷阱。无论你是初学者,还是希望提升代码质量的中级开发者,都能从中获得实用价值。


函数原型与参数详解

在使用任何函数之前,了解它的“签名”是必须的。fgets() 的函数原型如下:

char *fgets(char *str, int n, FILE *stream);

这个原型虽然简短,但每个参数都承载着关键信息。让我们逐个拆解:

  • char *str:这是你要存放读取内容的字符数组指针。它必须是一个已分配内存的字符数组,比如 char buffer[256];
  • int n:表示最多可以读取的字符数量(包括结尾的 \0 字符)。注意:n 是最大容量,实际读取的字符数可能更少。
  • FILE *stream:表示数据来源,可以是标准输入 stdin,也可以是文件指针(如 fopen() 打开的文件)。

💡 小贴士fgets() 会自动在读取的内容末尾添加一个 \0,因此你不需要手动添加。但前提是 n 足够大,至少要留出一个字节给 \0


从 stdin 读取用户输入:最常见用法

最常见的使用场景是读取用户的整行输入。比如,你想让用户输入一段包含空格的句子,比如“今天天气真好”。

示例代码:读取用户输入的一整行

#include <stdio.h>

int main() {
    char input[100];  // 定义一个长度为 100 的字符数组

    printf("请输入一段文字(支持空格):");

    // 使用 fgets 从标准输入读取一行,最多读取 99 个字符
    // 100 是数组大小,留 1 个位置给 '\0'
    if (fgets(input, 100, stdin) != NULL) {
        // 成功读取后,打印内容
        printf("你输入的内容是:%s", input);
    } else {
        // 读取失败,可能是输入结束(如 Ctrl+D)
        printf("读取输入失败。\n");
    }

    return 0;
}

代码注释说明:

  • char input[100];:定义一个字符数组,用于存储输入内容。数组大小为 100,意味着最多能存 99 个有效字符 + 1 个 \0
  • fgets(input, 100, stdin):从标准输入(键盘)读取数据,最多读取 99 个字符(因为要留 1 个给 \0)。
  • if (fgets(...) != NULL):检查函数是否成功读取。如果用户通过 Ctrl+D(Unix/Linux)或 Ctrl+Z(Windows)结束输入,fgets() 会返回 NULL
  • printf("你输入的内容是:%s", input);:输出用户输入的内容。注意:fgets() 会保留换行符(\n),如果不想显示换行,可以后续处理。

处理换行符:一个容易被忽略的细节

fgets() 有个“隐藏特性”——它会把输入中的换行符 \n 也读进来,并存入缓冲区。这在某些场景下会带来困扰。

示例:换行符带来的问题

#include <stdio.h>

int main() {
    char line[100];

    printf("请输入一句话:");
    fgets(line, 100, stdin);

    // 输出时,会看到换行符被打印出来了
    printf("你输入的是:%s", line);

    return 0;
}

假设用户输入 Hello World,按下回车,输出将是:

你输入的是:Hello World

注意:Hello World 后面有一个换行,这其实是 fgets() 读进来的 \n

解决方案:手动移除换行符

#include <stdio.h>
#include <string.h>

int main() {
    char line[100];

    printf("请输入一句话:");
    if (fgets(line, 100, stdin) != NULL) {
        // 检查是否读到了换行符
        int len = strlen(line);
        if (len > 0 && line[len - 1] == '\n') {
            line[len - 1] = '\0';  // 将换行符替换为字符串结束符
        }

        printf("处理后的输入:%s\n", line);
    }

    return 0;
}

✅ 这种处理方式非常实用,尤其在需要判断输入长度或进行字符串比较时。


从文件中读取内容:真实项目场景

fgets() 的另一个强大用途是读取文件内容。相比 fscanf(),它更适合处理含空格的文本行。

示例:读取一个文本文件的每一行

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *file = fopen("data.txt", "r");  // 以只读方式打开文件

    if (file == NULL) {
        printf("无法打开文件!\n");
        return 1;
    }

    char line[256];  // 每行最多 255 个字符

    // 循环读取文件中的每一行
    while (fgets(line, 256, file) != NULL) {
        // 去除换行符
        int len = strlen(line);
        if (len > 0 && line[len - 1] == '\n') {
            line[len - 1] = '\0';
        }

        printf("第 %d 行:%s\n", line_count++, line);
    }

    fclose(file);  // 关闭文件,释放资源
    return 0;
}

说明:

  • fopen("data.txt", "r"):尝试打开名为 data.txt 的文件,若不存在或权限不足,返回 NULL
  • while (fgets(line, 256, file) != NULL):循环读取,直到文件结束(EOF)。
  • fclose(file):必须调用,避免资源泄漏。

📌 建议:在实际项目中,文件路径应使用相对路径或通过参数传入,避免硬编码。


常见错误与安全陷阱

虽然 fgets()gets() 安全得多,但使用不当仍会引发问题。

常见错误 问题描述 正确做法
数组大小小于 n 例如 char buf[10]; fgets(buf, 20, stdin); 会导致缓冲区溢出 确保 n 小于等于数组大小
忘记处理换行符 输出时出现多余换行 strlen 检查并替换 \n\0
忽略返回值 不检查 fgets() 是否成功 始终判断返回值是否为 NULL
未关闭文件 导致资源泄漏或文件损坏 用完文件后调用 fclose()

总结:为什么 fgets() 是推荐之选?

在 C 语言中,fgets() 是处理字符串输入的“黄金标准”。相比 scanf(),它能安全读取含空格的字符串;相比 gets(),它不会导致缓冲区溢出;相比 fscanf(),它更适合作为行读取工具。

无论你是写一个简单的命令行程序,还是处理日志文件、配置文件,fgets() 都能胜任。掌握它的用法,是你迈向专业 C 编程的重要一步。

记住:安全的输入,是高质量程序的起点。从今天开始,让 fgets() 成为你代码中的“守护者”。