C语言的单字符IO之getchar()和putchar()
引言
getchar()和putchar()每次只处理一个字符。你可能认为这种方法很低级,与我们的阅读方式相差甚远。但是,这种方法很适合计算机,而且这是绝大多数文本处理程序所用的核心方法。
示例分析
/* echo.c -- repeats input */
#include <stdio.h>
int main(void)
{
char ch;
while ((ch = getchar()) != '#')
putchar(ch);
return 0;
}
该程序获取键盘输入的字符,并把这些字符发送到屏幕上。程序使用while循环,当读到#
字符时停止。
自从ANSI C标准发布以后,c就把stdio.h头文件与使用getchar()和putchar()相关联,因此程序中要包含这个头文件(其实,getchar()和putchar()都不是真正的函数,它们被定义为供预处理器使用的宏)。运行程序后,与用户的交互如下:
Hello,there. I would[用户输入]
Hello,there. I would
like a #3 bag of potatoes.[用户输入]
like a
为什么输入的字符能直接在屏幕上显示?如果用一个特殊字符(如,#
)来结束输入,就无法在文本中使用这个字符,是否有更好的方法结束输入?要回答这些问题,首先要了解C程序如何处理键盘输入,尤其是缓冲和标准输入文件的概念。
缓冲区
如果在一些老式系统运行上面的程序,你输入的文本时可能显示如下
HHeelllloo,,tthheerree.. II wwoouulldd
lliikkee aa #
像这样回显用户输入的字符后立即重复打印该字符是属于无缓冲(或直接)输入,即正在等待的程序可立即使用输入的字符。对于该例,大部分系统用户按下Enter键之前不会重复打印刚输入的字符,这种输入形式属于缓冲输入。用户输入的字符被收集并存储在一个被称为缓冲区的临时存储区,按下ENTER键后,程序才可使用用户输入的字符。如下图所示,
为什么要有缓冲区?首先,把若干字符作为一个块进行传输比逐个发送这些字符节约时间。其次,如果用户打错字符,可以直接通过键盘修正错误。当最后按下ENTER键时,传输的是正确的输入。
缓冲输入有这么多好处,为什么还要使用无缓冲输入呢?因为一些交互式程序需要无缓冲输入。例如,在游戏中,你希望按下一个键就执行相应的指令。当你玩LOL的时候不会希望每次放技能都要按下ENTER,你的英雄才动作吧!!!因此缓冲输入与无缓冲输入都有用武之地。
缓冲分两类:**完全缓冲I/O和行缓冲I/O。**完全缓冲输入指的是当缓冲区填被满时才刷新缓冲区(内容被发送至目的地),通常出现在文件输入中。缓冲区的大小取决于系统,常见的大小是512字节和4096字节。行缓冲I/O指的是在出现换行符时刷新缓冲区,键盘输入通常是行缓冲输入,所以按下ENTER键后才刷新缓冲区。
那么使用缓冲输入还是无缓冲输入?ANSI C和后续的C标准都规定输入是缓冲的,不过最初K&R把这个决定权交给了编译器的编写者。
ANSI 没有提供调用无缓冲输入的标准方式,这意味着是否能进行无缓冲输入取决于计算机系统。
参考资料:
[1] 史蒂芬・普拉达. C Primer Plus (第6版) 中文版[M]. 人民邮电出版社, 2016.