ZetCode

C getline 函数

最后修改日期:2025 年 4 月 6 日

逐行读取输入是 C 编程中的常见需求。`getline` 函数为此任务提供了一个强大的解决方案,可以自动处理内存分配。本教程将解释如何有效地使用 `getline`,涵盖其参数、返回值和内存管理。通过实际示例,您将学会如何安全高效地从文件和标准输入读取数据。

什么是 getline?

`getline` 函数从流中读取一整行,并将其存储在它自动分配的缓冲区中。它是 POSIX 标准的一部分,可在 GNU C 库中使用。与 `fgets` 不同,`getline` 动态处理内存分配,根据需要调整大小以适应长行。它返回读取的字符数,失败时返回 -1。使用后请务必释放缓冲区以防止内存泄漏。

基本的 getline 用法

此示例演示了从标准输入读取的最简单的 `getline` 用法。

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

int main() {
    char *line = NULL;
    size_t len = 0;
    ssize_t read;

    printf("Enter a line: ");
    read = getline(&line, &len, stdin);

    if (read != -1) {
        printf("You entered: %s", line);
    } else {
        printf("Error reading input\n");
    }

    free(line);  // Always free allocated memory
    return 0;
}

在这里,`getline` 接受三个参数:指向缓冲区的指针(`&line`)、指向缓冲区大小的指针(`&len`)以及输入流(`stdin`)。该函数根据需要分配内存并返回读取的字符数。换行符包含在输出中。完成后不要忘记 `free` 分配的内存。

从文件读取

学习如何使用 `getline` 高效地从文件中读取行。

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

int main() {
    FILE *fp;
    char *line = NULL;
    size_t len = 0;
    ssize_t read;

    fp = fopen("data.txt", "r");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }

    while ((read = getline(&line, &len, fp)) != -1) {
        printf("Line length: %zu\n", read);
        printf("Content: %s", line);
    }

    free(line);
    fclose(fp);
    return 0;
}

此示例打开一个文件并使用 `getline` 逐行读取。循环一直持续到 `getline` 返回 -1(EOF)。每行的长度和内容都会被打印出来。请注意,`len` 由 `getline` 自动管理,根据需要为更长的行进行增长。使用后,缓冲区和文件都会被正确关闭。

处理长行

`getline` 会自动为任意长度的行处理内存分配。此示例演示了此功能。

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

int main() {
    char *line = NULL;
    size_t len = 0;
    ssize_t read;

    printf("Enter a very long line: ");
    read = getline(&line, &len, stdin);

    if (read != -1) {
        printf("Buffer size: %zu\n", len);
        printf("Line length: %zu\n", read);
    }

    free(line);
    return 0;
}

与固定大小的缓冲区不同,`getline` 会动态调整其缓冲区以适应任何长度的输入。`len` 参数显示分配的缓冲区大小,该大小可能大于实际行长度。这种方法可以防止缓冲区溢出,并简化了对不可预测输入大小的处理。

处理每个字符

此示例显示了如何处理由 `getline` 读取的行中的单个字符。

process_chars.c
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int main() {
    char *line = NULL;
    size_t len = 0;
    ssize_t read;

    printf("Enter text: ");
    read = getline(&line, &len, stdin);

    if (read != -1) {
        for (size_t i = 0; i < read; i++) {
            printf("%c ", toupper(line[i]));
        }
        printf("\n");
    }

    free(line);
    return 0;
}

使用 `getline` 读取一行后,我们可以单独访问每个字符。此示例使用 `toupper` 将每个字符转换为大写。循环从 0 到 `read-1` 运行,覆盖行中的所有字符(包括换行符)。请记住,`read` 表示读取的字符数,而 `len` 显示分配的缓冲区大小。

移除换行符

`getline` 会将换行符包含在输出中。此示例演示了如何根据需要将其移除。

remove_newline.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    char *line = NULL;
    size_t len = 0;
    ssize_t read;

    printf("Enter text: ");
    read = getline(&line, &len, stdin);

    if (read != -1) {
        // Remove newline if present
        if (line[read - 1] == '\n') {
            line[read - 1] = '\0';
            read--;
        }
        printf("Text without newline: %s\n", line);
        printf("Length: %zu\n", read);
    }

    free(line);
    return 0;
}

可以通过检查最后一个字符是否为 `'\n'` 并将其替换为 null 终止符来移除换行符。这会就地修改行并调整长度。当您需要处理不带换行符的行内容时(例如进行字符串比较或进一步操作),此技术非常有用。

读取多行

此示例演示了读取多行直到满足特定条件。

multiple_lines.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    char *line = NULL;
    size_t len = 0;
    ssize_t read;

    printf("Enter lines (type 'quit' to exit):\n");
    while ((read = getline(&line, &len, stdin)) != -1) {
        // Remove newline for comparison
        if (line[read - 1] == '\n') {
            line[read - 1] = '\0';
        }

        if (strcmp(line, "quit") == 0) {
            break;
        }

        printf("You typed: %s\n", line);
    }

    free(line);
    return 0;
}

该程序会连续读取输入,直到用户输入“quit”。在比较之前,每行都会通过移除换行符进行处理。同一缓冲区会为每一行重复使用,`getline` 会根据需要自动管理内存分配和重新分配。这种模式在交互式程序和文本处理器中很常见。

错误处理

在使用 `getline` 时,正确的错误处理至关重要。此示例显示了健壮的错误检查。

error_handling.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main() {
    char *line = NULL;
    size_t len = 0;
    ssize_t read;

    errno = 0;  // Clear error state
    read = getline(&line, &len, stdin);

    if (read == -1) {
        if (errno == ENOMEM) {
            fprintf(stderr, "Error: Out of memory\n");
        } else if (feof(stdin)) {
            printf("End of input reached\n");
        } else {
            perror("Error reading input");
        }
    } else {
        printf("Read %zu characters\n", read);
    }

    free(line);
    return 0;
}

此示例演示了对 `getline` 的全面错误处理。它分别检查了内存耗尽(`ENOMEM`)和文件结束等特定错误条件。`errno` 变量和 `perror` 提供了详细的错误信息。在使用 `getline` 时,请务必检查错误,尤其是在生产代码中输入可能不可预测的情况下。

使用 getline 的最佳实践

来源

getline man 页

本教程探讨了通用的 `getline` 函数,从基本用法到高级技术。凭借自动内存管理和高效的行读取,`getline` 在许多用例中优于 `fgets` 等替代方法。掌握它将使您的 C 程序更加健壮和易于维护。

作者

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

列表 C 标准库