ZetCode

C fgetc 和 getc 函数

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

字符输入函数是 C 编程中用于从文件或标准输入读取数据的重要工具。fgetcgetc 函数提供了一次读取一个字符的高效方法。本教程将探讨它们的区别、正确用法和实际应用。掌握这些函数将提高您有效处理文本文件和用户输入的能力。

什么是 fgetc 和 getc?

fgetcgetc 函数从文件流中读取单个字符。两者都返回一个转换为 intunsigned char 类型的字符,或者在到达文件末尾或发生错误时返回 EOF。关键区别在于 getc 可以实现为宏,而 fgetc 始终是一个函数。这使得 fgetc 在复杂表达式中更安全,但在某些情况下比 getc 稍慢。

基本的 fgetc 示例

本示例演示了如何使用 fgetc 逐个字符地读取文件。

basic_fgetc.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("text.txt", "r");
    if (fp == NULL) {
        perror("Error opening file");
        return 1;
    }

    int c;
    while ((c = fgetc(fp)) != EOF) {
        putchar(c);
    }

    fclose(fp);
    return 0;
}

此程序以读取模式打开 "text.txt" 文件,并使用 fgetc 读取直到文件末尾 (EOF) 的每个字符。字符被存储在 int 类型中,以正确处理 EOF。然后使用 putchar 将每个字符打印到标准输出。最后,关闭文件以释放资源。

基本的 getc 示例

本示例展示了使用 getc 而不是 fgetc 实现的相同功能。

basic_getc.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("text.txt", "r");
    if (fp == NULL) {
        perror("Error opening file");
        return 1;
    }

    int c;
    while ((c = getc(fp)) != EOF) {
        putchar(c);
    }

    fclose(fp);
    return 0;
}

结构与 fgetc 示例相同,这表明了这些函数的可互换性。主要区别在于它们的实现方式,getc 可能是一个宏展开。在紧密的循环中,它们的性能可能会略有不同。

从标准输入读取

当与 stdin 一起使用时,这两个函数都可以从标准输入读取。

stdin_example.c
#include <stdio.h>

int main() {
    printf("Type some text (Ctrl+D to end):\n");

    int c;
    while ((c = fgetc(stdin)) != EOF) {
        putchar(c);
    }

    return 0;
}

此程序直接从标准输入读取,直到到达文件末尾(在 Unix/Linux 上按 Ctrl+D,在 Windows 上按 Ctrl+Z)。每个字符都会立即使用 putchar 回显。这演示了这些函数如何与不同类型的流一起工作,而不仅仅是文件。

计算文件中的字符数

这是一个使用 fgetc 计算文件中字符数的实际示例。

count_chars.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("document.txt", "r");
    if (fp == NULL) {
        perror("Error opening file");
        return 1;
    }

    int count = 0;
    while (fgetc(fp) != EOF) {
        count++;
    }

    printf("Total characters: %d\n", count);
    fclose(fp);
    return 0;
}

该程序打开一个文件并计算直到 EOF 的每个字符。计数包括所有字符,例如空格、制表符和换行符。这表明 fgetc 可用于简单的文件分析任务。请注意,对于包含多字节字符的文件,实际的字节计数可能会有所不同。

使用 fgetc 计算行数

此示例通过检测换行符来计算文件中的行数。

count_lines.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("code.c", "r");
    if (fp == NULL) {
        perror("Error opening file");
        return 1;
    }

    int lines = 0;
    int c;
    while ((c = fgetc(fp)) != EOF) {
        if (c == '\n') {
            lines++;
        }
    }

    // Count last line if file doesn't end with newline
    if (c == EOF && lines > 0) {
        lines++;
    }

    printf("Total lines: %d\n", lines);
    fclose(fp);
    return 0;
}

该程序扫描文件以检测换行符来计算行数。它包括一个特殊情况,用于处理不以换行符结尾的文件。这表明 fgetc 可用于更复杂的文本处理任务。该逻辑可以扩展到计算其他特定字符或模式。

fgetc 和 getc 之间的区别

本示例强调了这些函数使用方式的一个关键区别。

function_diff.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        perror("Error opening file");
        return 1;
    }

    // Safe with fgetc (function)
    int c1 = fgetc(fp);
    int c2 = fgetc(fp);

    // Potentially unsafe with getc (macro)
    // int c3 = getc(fp++);  // Would fail - fp++ evaluated multiple times

    printf("First two characters: %c %c\n", c1, c2);
    fclose(fp);
    return 0;
}

注释掉的行显示了为什么 getc 在某些情况下可能很危险。因为它可能是一个宏,带有副作用的参数(如 fp++)可能会被求值多次。fgetc 始终是安全的,因为它是一个真正的函数。此示例演示了何时优先选择其中一个。

使用 fgetc 进行错误处理

正确处理错误对于读取文件至关重要。本示例对此进行了演示。

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

int main() {
    FILE *fp = fopen("missing.txt", "r");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }

    int c;
    while ((c = fgetc(fp)) != EOF) {
        putchar(c);
    }

    if (ferror(fp)) {
        perror("Error reading file");
        fclose(fp);
        return 1;
    }

    fclose(fp);
    return 0;
}

读取循环之后,我们检查 ferror 以检测是否发生了任何读取错误(不仅仅是 EOF)。在处理重要文件时,这是良好的实践。该程序还首先检查文件打开错误。全面的错误处理可以使程序更加健壮,并且更容易调试。

使用 fgetc 和 getc 的最佳实践

来源

C fgetc 文档

本教程深入探讨了 fgetcgetc 函数,展示了它们的相似之处、区别和实际应用。这些基本的字符输入函数对于任何处理文本处理或文件 I/O 的 C 程序员来说都是必不可少的工具。

作者

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

列表 C 标准库