一、单字符IO
getchar()和putchar()每次都处理一个字符,如果输入多于一个字符,则只读取第一个字符
- getchar() 是从键盘中接收字符
- putchar() 是将接收的字符打印出来
#include <stdio.h>
int main(void)
{
char ch;
// 将接收的字符赋值给ch,如果ch不等于#,则继续输入。
// 如果有#,则#后面的内容不会被打印出来。
while ((ch = getchar()) != '#')
{
putchar(ch);
}
return 0;
}
运行结果:
它就是 scanf("%c", c)的替代品,除了更加简洁,没有其它优势了;或者说,getchar() 就是 scanf() 的一个简化版本
getchar其实返回的是字符的ASCII码值(整数)
getchar在读取结束或者失败的时候,会返回EOF注意:EOF意思是end of file,本质上是-1
二、缓冲区
什么是缓冲区:
- 缓冲区( Buffer) 又称为缓存( Cache) ,是内存空间的一部分。计算机在内存中预留了一定的存储 空间,用来暂时保存输入或输出的数据,这部分预留的空间就叫做缓冲区
- 缓冲是在两种不同速度设备之间传输信息时平滑传输过程的常用手段
- 缓冲区是治愈与输入输出有关的大部分疑难杂症的良药,它能使你对输入输出的认识上升到一个更高的层次
缓冲区位于用户程序和硬件设备之间,用来缓存数据,目的是让快速的 CPU 不必等待慢速的输入输出设备,同 时减少操作硬件的次数。对于 IO 密集型的网络应用程序,比如网站、数据库、 DNS、 CDN 等,缓冲区的设计 至关重要,它能十倍甚至一百倍得提高程序性能
为什么引入缓冲区?
- 为了解决高速设备和低速设备之间速度不匹配的问题,比如CPU和打印机;并降低输入输出设备的读写次数
- 当我们输入错误或打错字符时,可以通过键盘的删除键直接删除或回退,然后重新输入。当最后按下Enter键之后,输入的就是正确的字符。如果没有缓冲区,就没有办法修改
缓冲区类型
- 根据缓冲区对应的是输入设备还是输出设备,可以分为输入缓冲区和输出缓冲区
- 根据数据刷新的时机,可以分为全缓冲、行缓冲、 不带缓冲
全缓冲
- 当缓冲区被填满以后才进行真正的输入输出操作。缓冲区的大小都有限制的,比如 4KB、 4MB等,数据量达到最大值时就清空缓冲区
- 典型代表是对硬盘文件的读写,在后续章节会讲解
行缓冲
- 当在输入或者输出的过程中遇到换行符时,才执行真正的输入输出操作
- 典型代表就 是标准输入设备(也即键盘)和标准输出设备(也即显示器)
- 对于 scanf(),不管用户输入多少内容,只要不按下回车键,就不进行真正的读取。这是因为 scanf() 是带有 行缓冲的,用户输入的内容会先放入缓冲区,直到用户按下回车键,产生换行符\n,才会刷新缓冲区,进行真 正的读取
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("一瓢不饮");
sleep(2);
printf("\n");
return 0;
}
运行结果:
运行程序后,会发现第一个 printf() 并没有立即输出,而是等待 2 秒以后,和第二个 printf() 一起输出了
究其原因,就是 printf() 带有行缓冲区,注意最后的换行符\n,它会使得缓冲区刷新,将缓冲区中的所有内容都输出到显示器上
不带缓冲
- 不带缓冲区,数据就没有地方缓存,必须立即进行输入输出
- Windows 下的 printf() 不带缓冲区,不管最后有没有换行符\n,都会立即输出,所以对于类似的输出代码, Windows 和 Linux、 Mac OS 会有不同的表现
- 错误信息输出函数 perror() 也没有缓冲区。错误信息必须刻不容缓、立即、马上显示出来
%d和%c读取缓冲区的差别
- 对于 %d,在缓冲区中,空格、回车、Tab 键都只是分隔符,不会被 scanf 当成数据取用。%d 遇到它们就跳过,取下一个数据。但是如果是 %c,那么空格、回车、Tab 键都会被当成数据输出给 scanf 取用
# include <stdio.h>
int main()
{
int a,c;
char b;
scanf("%d%c%d",&a,&b,&c);
printf("a = %d, b = %c, c = %d\n",a,b,c);
return 0;
}
运行结果:
在此程序中,原本希望的是将数字 1 赋给变量 a,将字符 '5' 赋给变量 b,将数字 6 赋给变量 c。但从输出结果可以看出,按一下回车,scanf 开始到缓冲区中取数据,因为“输入参数”有三个,所以 scanf 从缓冲区中取三个数据。数字 1 赋给变量 a,而因为变量 b 是 %c,所以将空格键赋给变量 b,然后数字 5 赋给变量 c,而数字 6 仍然在缓冲区中,等待下一个 scanf 来取
1. 清空/刷新缓冲区
所谓刷新缓冲区,就是将缓冲区中的内容送达到目的地。缓冲区的刷新遵循以下的规则:
- 不管是行缓冲还是全缓冲,缓冲区满时会自动刷新;
- 行缓冲遇到换行符\n 时会刷新;
- 关闭文件时会刷新缓冲区;(linux操作系统文件IO)
- 程序关闭时一般也会刷新缓冲区,这个是由标准库来保障的;
- 使用特定的函数也可以手动刷新缓冲区
- fflush() 是一个专门用来清空输出缓冲区的函数, stdout 是 standard output 的缩写,表示标准输出设备,也即显示器。
- 清空输入缓冲区:很遗憾地说,没有一种既简洁明了又适用于所有平台的清空输入缓冲区的方案,只能将输入缓冲区中的数据都读取出来,但是却不使用。
使用 getchar() 清空缓冲区:
每次从缓冲区中读取一个字符,包括空格、制表符、换行符等空白符,只要让 getchar() 不停地读取,直到读完缓冲区中的所有字符,就能达到清空缓冲区的效果
#include <stdio.h>
int main()
{
int a,b;
char c;
scanf("%d",&a);
while((c=getchar())!='\n' && c!=EOF);
scanf("%d",&b);
printf("a=%d , b=%d\n",a,b);
return 0;
}
运行结果:
三、结束键盘输入
- C语言怎么判断输入的结束?
- 在输入前约定输入个数
- 约定输入结束标记(比如 以空格 换行 或者EOF作为结束标记)
- 怎么用EOF结束输入?
- 在linux下直接按CTRL+D快捷键
- EOF定义在stdio.h文件中:
#define EOF (-1)