为何使用fgets连续读取字符串后需读取换行符才能继续读取?
fgets()的换行符处理 + 输入缓冲区的残留 嘿,这个问题我太熟了——本质上是fgets()的核心行为特性在搞鬼,我给你掰扯清楚:
fgets()的读取规则:它会从输入流(比如stdin)里读取字符,直到触发三个条件之一:1. 读到换行符\n;2. 读取的字符数达到缓冲区长度-1(要留位置给终止符\0);3. 到达输入流末尾。关键是:它会把遇到的换行符\n一起读到缓冲区里,不会丢弃。输入流的实际结构:你说每行输入后仅按一次回车,那输入流里的内容实际是这样的(用字符+ASCII码表示):
A g a t h a C h r i s t i e \n(10) U K \n(10) [下一行内容] \n(10)这里的
\n就是你按回车产生的换行符。
现在假设你连续调用两次fgets():
- 第一次调用:只要缓冲区够大,
fgets()会把Agatha Christie\n全部读进缓冲区,此时输入流里剩下UK\n[下一行内容]\n。 - 第二次调用:
fgets()会把UK\n读进缓冲区,输入流里剩下[下一行内容]\n。
那为什么你会觉得“必须读取换行符才能读取下一行”?大概率是以下两种情况之一:
情况1:缓冲区太小导致的部分读取残留
如果你的缓冲区长度比Agatha Christie\n的总长度(比如"Agatha Christie"是15个字符,加换行符共16个)还小,那第一次fgets()只能读满缓冲区,剩下的字符(包括换行符)会留在输入流里。比如缓冲区是10,第一次读了前9个字符+\0,剩下的thie\nUK\n...还在输入流里。这时候第二次fgets()会读剩下的部分直到换行符,你得到的"UK"会和残留字符混在一起,之后输入流里还留着换行符,导致你必须先读这个换行符才能拿到下一行内容。
情况2:混淆了缓冲区和输入流里的换行符
当你用fgets()读到Agatha Christie\n后,可能会手动把缓冲区里的\n替换成\0(比如用strchr(buf, '\n')处理),得到干净的"Agatha Christie"——但这只是修改了内存里的缓冲区内容,输入流里的对应内容已经被读完了。第二次读UK\n同理。如果这时候你觉得需要先读换行符才能读下一行,大概率是输入时在UK那行之后多按了一次回车(输入流里多了一个\n),导致下一次fgets()先读到空的换行符行,再读到真正的下一行内容。
验证方法
你可以在每次调用fgets()后,打印缓冲区的原始内容(包括不可见字符),直观看到读取的所有字符:
#include <stdio.h> #include <string.h> int main() { char buf[100]; // 第一次读 fgets(buf, sizeof(buf), stdin); printf("第一次读取的原始内容(含ASCII码):"); for (int i = 0; i < strlen(buf); i++) { printf("%c(%d) ", buf[i], buf[i]); } printf("\n"); // 第二次读 fgets(buf, sizeof(buf), stdin); printf("第二次读取的原始内容(含ASCII码):"); for (int i = 0; i < strlen(buf); i++) { printf("%c(%d) ", buf[i], buf[i]); } printf("\n"); // 第三次读 fgets(buf, sizeof(buf), stdin); printf("第三次读取的原始内容(含ASCII码):"); for (int i = 0; i < strlen(buf); i++) { printf("%c(%d) ", buf[i], buf[i]); } printf("\n"); return 0; }
运行后输入你的内容,就能清楚看到每次fgets()读到的字符(换行符的ASCII码是10),直接定位输入流里的残留问题。
总结一下:fgets()本身会处理换行符,所谓“必须读取换行符才能读下一行”,要么是缓冲区太小导致的部分读取残留,要么是输入时多输入了换行符,或是你混淆了缓冲区和输入流的内容。
内容的提问来源于stack exchange,提问作者NuSuntStudent

