C fgetc 和 getc 函数
最后修改日期:2025 年 4 月 6 日
字符输入函数是 C 编程中用于从文件或标准输入读取数据的重要工具。fgetc 和 getc 函数提供了一次读取一个字符的高效方法。本教程将探讨它们的区别、正确用法和实际应用。掌握这些函数将提高您有效处理文本文件和用户输入的能力。
什么是 fgetc 和 getc?
fgetc 和 getc 函数从文件流中读取单个字符。两者都返回一个转换为 int 的 unsigned char 类型的字符,或者在到达文件末尾或发生错误时返回 EOF。关键区别在于 getc 可以实现为宏,而 fgetc 始终是一个函数。这使得 fgetc 在复杂表达式中更安全,但在某些情况下比 getc 稍慢。
基本的 fgetc 示例
本示例演示了如何使用 fgetc 逐个字符地读取文件。
#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 实现的相同功能。
#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 一起使用时,这两个函数都可以从标准输入读取。
#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 计算文件中字符数的实际示例。
#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 计算行数
此示例通过检测换行符来计算文件中的行数。
#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 之间的区别
本示例强调了这些函数使用方式的一个关键区别。
#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 进行错误处理
正确处理错误对于读取文件至关重要。本示例对此进行了演示。
#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 的最佳实践
- 将返回值存储在 int 中:始终使用
int来存储结果,以便正确处理 EOF。 - 对于复杂表达式,首选 fgetc:当参数可能带有副作用时,使用
fgetc。 - 检查错误:循环后使用
ferror检测读取错误。 - 考虑性能:在性能关键的循环中,
getc可能稍快一些。 - 正确关闭文件:完成后务必使用
fclose关闭文件。
来源
本教程深入探讨了 fgetc 和 getc 函数,展示了它们的相似之处、区别和实际应用。这些基本的字符输入函数对于任何处理文本处理或文件 I/O 的 C 程序员来说都是必不可少的工具。
作者
列表 C 标准库。