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