C 库函数 – fgets()(超详细)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

在 C 语言编程中,输入输出操作是开发者必须掌握的核心技能之一。fgets() 函数作为 C 标准库中用于读取字符串的工具,其功能强大且适用场景广泛。无论是从文件读取数据,还是从标准输入接收用户输入,fgets() 都能提供安全、灵活的解决方案。然而,许多开发者在初次接触该函数时,容易因参数设置不当或边界条件处理不足而引发问题。本文将从基础概念、使用场景、代码示例到常见陷阱,系统性地解析 fgets() 的核心知识点,并通过生动的比喻和实践案例,帮助读者快速掌握这一工具。


函数原型与参数解析

fgets() 函数的完整原型为:

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

该函数接受三个参数:

  1. str:指向字符数组的指针,用于存储读取到的字符串。
  2. n:表示最多读取的字符数(包括终止符 \0)。
  3. stream:指向 FILE 结构的指针,表示输入源,如标准输入 stdin 或文件流。

参数的“容量”与“安全边界”

n 参数想象为一个“容器的容量”。例如,若 n 设为 10,则 fgets() 最多会读取 9 个字符(第 10 个位置留给终止符 \0)。这种设计类似于给水杯设定最大容量,确保液体不会溢出,从而避免缓冲区溢出漏洞。


基础用法与代码示例

从标准输入读取字符串

以下示例演示如何通过 fgets() 读取用户输入:

#include <stdio.h>  

int main() {  
    char input[100]; // 定义足够大的缓冲区  
    printf("请输入一行文字:");  
    fgets(input, sizeof(input), stdin); // 读取输入到 input 数组  
    printf("您输入的内容是:%s", input);  
    return 0;  
}  

关键点解析

  • sizeof(input) 返回数组总长度(100),确保读取不超过缓冲区容量。
  • 若输入内容超过 n-1 字符,fgets() 会自动截断多余字符,并添加 \0

从文件读取数据

fgets() 也能用于文件操作。例如,逐行读取文件内容:

#include <stdio.h>  

int main() {  
    FILE *file = fopen("data.txt", "r");  
    if (file == NULL) {  
        perror("无法打开文件");  
        return 1;  
    }  

    char line[256];  
    while (fgets(line, sizeof(line), file) != NULL) {  
        printf("%s", line); // 输出文件的每一行  
    }  

    fclose(file);  
    return 0;  
}  

:循环条件 fgets(...)!=NULL 可检测到文件结束或错误。


gets() 的对比:安全性至关重要

C 标准库中存在另一个字符串输入函数 gets(),其原型为 char *gets(char *str)。然而,gets() 完全不检查缓冲区边界,若输入内容超过数组容量,将引发缓冲区溢出,导致程序崩溃或安全漏洞。因此,C11 标准已弃用 gets(),而 fgets() 因其安全性成为推荐替代方案。


深入细节:换行符与空格处理

换行符的保留与截断

fgets() 会读取输入中的换行符 \n,并将其存储在字符串中。例如,若用户输入“Hello World\n”,则 input 数组的内容为 "Hello World\n\0"。若希望去除换行符,可通过以下代码处理:

// 移除字符串末尾的换行符  
size_t len = strlen(input);  
if (len > 0 && input[len-1] == '\n') {  
    input[len-1] = '\0';  
}  

空格与空白字符的保留

fgets() 保留输入中的所有空白字符,包括空格、制表符和换行符。这使得它适合读取包含空格的完整句子或行。


常见问题与解决方案

问题 1:输入被截断

现象:输入内容超过 n-1 字符时,字符串被截断。
解决方案

  • 增大缓冲区容量(如使用 char buffer[1024])。
  • 在截断后,通过 fflush(stdin) 或循环清理输入流中的剩余字符(需注意平台兼容性)。

问题 2:读取空行或未读取到内容

现象:若输入流中存在残留字符(如前一次输入未处理的换行符),fgets() 可能直接读取这些残留,导致空行或意外结果。
解决方案

// 清理输入流中的残留字符  
int c;  
while ((c = getchar()) != '\n' && c != EOF);  

进阶技巧:灵活控制读取行为

1. 动态缓冲区管理

通过 malloc() 动态分配缓冲区,避免固定大小的限制:

#include <stdlib.h>  
#include <string.h>  

char *read_line(FILE *stream) {  
    char *buffer = malloc(100 * sizeof(char));  
    if (fgets(buffer, 100, stream) == NULL) {  
        free(buffer);  
        return NULL;  
    }  

    // 若输入超过 99 字符,重新分配内存  
    while (strlen(buffer) == 99 && buffer[99] != '\n') {  
        buffer = realloc(buffer, 2 * strlen(buffer) + 1);  
        fgets(buffer + strlen(buffer), strlen(buffer)+1, stream);  
    }  

    return buffer;  
}  

此示例通过动态扩展缓冲区,应对超长输入。

2. 读取指定长度的内容

若需读取固定长度的字符串(如密码输入),可结合 fgets()strncpy()

char password[10];  
printf("请输入密码(最多9字符):");  
fgets(password, sizeof(password), stdin);  
// 移除换行符并截断  
password[strcspn(password, "\n")] = '\0';  

性能与内存优化

缓冲区大小的权衡

  • 过小的缓冲区:频繁截断可能导致用户体验下降(如需要多次输入)。
  • 过大的缓冲区:占用过多内存,尤其在内存受限的嵌入式系统中需谨慎。

文件读取的效率优化

若需高效读取大文件,可结合 fread()mmap(),但需权衡代码复杂度与性能需求。


实战案例:实现简易文本编辑器

以下代码演示如何利用 fgets() 构建一个基础文本编辑器:

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

#define MAX_LINE 256  

void edit_file(const char *filename) {  
    FILE *file = fopen(filename, "r+");  
    if (!file) {  
        perror("文件打开失败");  
        return;  
    }  

    char lines[100][MAX_LINE]; // 存储文件的每一行  
    int count = 0;  
    char line[MAX_LINE];  

    // 读取文件内容到数组  
    while (fgets(line, MAX_LINE, file)) {  
        strcpy(lines[count++], line);  
    }  

    // 编辑操作(此处简化为添加新行)  
    printf("输入新内容:");  
    fgets(line, MAX_LINE, stdin);  
    strcpy(lines[count++], line);  

    // 重写文件  
    rewind(file);  
    for (int i = 0; i < count; i++) {  
        fputs(lines[i], file);  
    }  
    fclose(file);  
}  

int main() {  
    edit_file("test.txt");  
    return 0;  
}  

此案例展示了 fgets() 在文件读写和文本处理中的实际应用。


结论

fgets() 函数凭借其安全性、灵活性和广泛适用性,成为 C 语言开发者处理字符串输入的首选工具。通过合理设置缓冲区容量、处理换行符、避免常见陷阱,并结合动态内存管理等进阶技巧,开发者可以高效且安全地实现复杂输入输出场景。掌握 fgets() 的核心逻辑与细节,不仅能提升代码质量,更能为后续学习更复杂的 C 标准库函数打下坚实基础。建议读者通过实际编码练习,逐步加深对这一函数的理解与应用。

最新发布