scanf 从输入缓冲区读取数据
首先,要清楚的是,scanf在读取数据的时候,不是从键盘上读取,而是从
输入缓冲区读取数据
。
数据的接收
我们从键盘上输入的全部数据,不管是数字
还是字母
还是空格
、回车
、Tab键
等,操作系统在接收时,都是将它们当成字符
来接收的。
比如,我们从键盘输入123,它表示的并不是数字123,而是
字符'1'
、字符'2'
、字符'3'
。
数据存入缓冲区
在scanf中,从键盘中输入的一切数据,,不管是数字、字母、还是空格回车、Tab键等这些字符都会被当作数据存入缓冲区
。
当按下
回车键
时,scanf开始进入缓冲区读取数据,从前往后,依次取。
scanf 中%d读取数据
scanf中%d读取数据时,%d只识别“十进制整数”。
对于%d而言,空格、回车、Tab键都是区分数据与数据的分隔符
。
当scanf进入缓冲区读取数据的时候,如果%d遇到空格、回车、Tab键,那么它并不会采用,而是跳过取后面的数据,直到取到“十进制整数”
为止。
%d以
十进制整数形式
读取整数,scanf函数依然是每次读取一个字符
,而不是读取一整个数字,不会马上结束读取进行存储。首先scanf函数从第一个输入开始检查,它会跳过所有空白字符,直到它发现一个数字或者符号(+或-),它便会保存该字符然后往下读取,如果接下来读取依然是数字,他就会不断地保存然后读取下一个字符直到遇见一个非数字字符,scanf函数认为这里是数字的结尾。
大家可以参考一个博主的这篇文章scanf函数到底怎么读取数字?
对于被跳过和取出的数据,系统会将它从缓冲区中
释放掉
。
未被跳过或取出的数据,系统会将它一直放在缓冲区,直到下一个scanf来获取。%d读取时中遇到字母,那么它不会跳过也不会取用,而是直接
从缓冲区跳出
。下面举个例子:
#include <stdio.h> int main() { int a,b; scanf("%d", &a ); printf("a=%d ", a); scanf("%d",&b); printf("a=%d",b); return 0; }
当输入1(空格)2(回车)时,输出结果为
——————————————
a=1 b=2
——————————————
当输入1(Tab)2(回车)时,输出结果为
——————————————
a=1 b=2
——————————————
当输入(空格)(空格)1(空格)2(回车)时,输出结果为
——————————————
a=1 b=2
——————————————
当输入1(回车)2(回车)时,输出结果为
——————————————
1
a=1
2
a=2
——————————————
从输出结果可以看出来,不管是多种形式123的次性全部输入完,还是一个一个输入4,输出结果都是一样的。
原因在于从键盘上输入的数据都会被依次存入缓冲区,不管是数字还是字符都会被当作数据存进去。
我们可以全部将数据全部存入缓冲区后再一个一个用scanf取出,也可以到缓冲区存入一个数据scanf读取一个数据再到缓冲区存入一个数据再用scanf读取一个数据。
%d读取时中遇到字母
,那么它不会跳过也不会取用,而是直接从缓冲区跳出
。
#include <stdio.h> int main() { int a,b; scanf("%d", &a ); printf("a=%d ", a); scanf("%d",&b); printf("a=%d",b); return 0; }
当我们输入
a(空格)1(回车)时,输出为
——————————————
a=-858993460 b=-858993460
——————————————
为什么会是这样的结果呢?
scanf中%d从缓冲区读取数据,从前往后依次取,先读到的字符是字母a,那么它不会跳过也不会取用
,而是直接从缓冲区跳出
,那么变量a没有值,即未被初始化,变量b也没有值,所以输出a=-858993460 b=-858993460
scanf中%c读取数据
对于%d,在缓冲区,空格,回车,Tab键都只是分隔符,不会被scanf当成数据取用,%d遇到它们就跳过,取下一个数据.但是如果是%c,那么空格,回车,Tab键都会被当成数据输出给scanf取用.
#include <stdio.h> int main() { char a,b; scanf("%c", &a ); printf("a=%c ", a); scanf("%c",&b); printf("a=%c",b); return 0; }
当我们输入1(空格)2()回车时,输出结果为
——————————————
a=1 b=
——————————————
这是因为,当我们输入结束时,输入缓冲区
的数据为1(空格)2(回车)
第一个scanf语句scanf从缓冲区读取一个字符,即字符1,将a=1 打印到屏幕上.
第二句scanf依然要从缓冲区读取数据,这时缓冲区还有数据,为(空格)2(回车),所以不需要我们再次从键盘上进行输入.scanf从缓冲区读取(空格)
,将此赋给变量b,b= 打印到屏幕上.
此时缓冲区剩下了2(回车).
但我们希望的是,将字符2赋值给变量b,所以我们希望将(空格)清理掉,让scanf读取字符2.
清空输入缓冲区
例子
下面,先看一个例子
我们要实现用户输入密码,输入完之后,需要用户进行确认(Y/N)
#include <stdio.h> int main() { char password[20]; printf("请输入密码:>"); scanf("%s", password); printf("请确认(Y/N):"); char ch=0; scanf("%c",&ch); if (ch == 'Y') printf("确认成功\n"); else printf("确认失败\n"); return 0; }
当我们运行程序,运行结果为
我们还没有输入Y或者N,为什么会直接弹出来确认失败
呢?
这是因为当我们输入密码> 123456(回车)时,scanf开始从输入缓冲区读取数据,第一个scanf语句,它只是读走了"123456",缓冲区剩下了(回车),对于第二个scanf语句,scanf依然从缓冲区读取数据,将留下缓冲区的(回车)读走.
所以我们希望我们可以清理先清理掉(回车)
,然后让用户自己输入Y/N.
用getchar()吸收回车
当我们要从输入流中取一个字符,但在之前使用过scanf,那么此时就必须要先用getchar()吸收回车。否则取到的将不是你想要的字符,而是scanf遗留在输入流中的回车。
我们对上面的程序进行修改:
#include <stdio.h> int main() { char password[20]; printf("请输入密码:>"); scanf("%s", password); getchar(); //(1) printf("请确认(Y/N):"); char ch=getchar(); //(2) if (ch == 'Y') printf("确认成功\n"); else printf("确认失败\n"); return 0; }
对于语句(1),我们用getchar()吸收scanf在缓冲区留下的回车
,简单,方便,都不需要将从缓冲区读到的回车赋值给一个字符变量.
对于语句(2),用getchar()
从缓冲区读取一个字符
,将此赋值给字符变量ch.
修改后的结果为:
练习
题目一
题目描述
描述
KiKi想完成字母大小写转换,有一个字符,判断它是否为大写字母,如果是,将它转换成小写字母;反之则转换为大写字母。
输入描述:
多组输入,每一行输入一个字母。
输出描述:
针对每组输入,输出单独占一行,输出字母的对应形式。
代码实现(方法一)-----用scanf获取字符
#include <stdio.h> int main() { char ch; while(scanf("%c",&ch) != EOF) { getchar();//吸收回车 if (ch>='a' && ch<='z') ch=ch-32; else if(ch>='A' && ch<='Z') ch =ch+32; printf("%c\n",ch); } return 0; }
代码实现(方法二)----用getchar()获取字符
#include <stdio.h> int main() { char ch; while((ch=getchar())!=EOF) { getchar();//吸收回车 if (ch>='a' && ch<='z') ch=ch-32; else if(ch>='A' && ch<='Z') ch =ch+32; printf("%c\n",ch); } return 0; }
代码实现(方法三)—使用库函数
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<ctype.h> //有库函数可以判断大写字母 -isupper //有库函数可以判断小写字母 -islower //大写转小写 -tolower //小写转大写 -toupper int main() { char ch = 0; while(scanf("%c", &ch) == 1) { if (islower(ch)) printf("%c\n",toupper(ch)); else if (isupper(ch)) printf("%c\n", tolower(ch)); } return 0; }
题目二
题目描述
描述
KiKi想判断输入的字符是不是字母,请帮他编程实现。
输入描述:
多组输入,每一行输入一个字符。
输出描述:
针对每组输入,输出单独占一行,判断输入字符是否为字母,输出内容详见输出样例。
#include<stdio.h> int main() { char a; while((a=getchar())!=EOF) { getchar();//吸收回车 if(('a'<=a&&a<='z')||('A'<=a&&a<='Z')) { printf("%c is an alphabet.\n",a); } else { printf("%c is not an alphabet.\n",a); } } }
当然这个代码也可以修改一下,不吸收回车
#include<stdio.h> int main() { char a; while((a=getchar())!=EOF) { if(('a'<=a&&a<='z')||('A'<=a&&a<='Z')) { printf("%c is an alphabet.\n",a); } else if { printf("%c is not an alphabet.\n",a); } } }
用 if 和 else if,这样我们没有处理的回车,也不会满足其中的任何一个条件,而执行语句.
同时,也可以利用库函数:
#include <stdio.h> #include <ctype.h> int main() { char a; while ((a = getchar()) != EOF) { getchar();//吸收回车 if (isupper(a) > 0 || islower(a) > 0) { printf("%c is an alphabet.\n", a); } else { printf("%c is not an alphabet.\n", a); } } return 0; }