清空输入缓冲器,C语言清空输入缓冲区完全攻略

清空输入缓冲器,C语言清空输入缓冲区完全攻略,第1张

概述本节,在学习如何清空输入缓冲区之前,我们先来介绍一下输入缓冲区的概念。 输入缓冲区 所有从键盘输入的数据,不管是字符还是数字,都是先存储在内存的缓冲区中,叫作 键盘输 本节,在学习如何清空输入缓冲区之前,我们先来介绍一下“输入缓冲区”的概念。
输入缓冲区所有从键盘输入的数据,不管是字符还是数字,都是先存储在内存的缓冲区中,叫作“键盘输入缓冲区”,简称“输入缓冲区”或“输入流”。我们先来看一个程序:
# include <stdio.h>int main(voID){    int a,b,c;    scanf("%d",&a);    printf("a = %d\n",a);    scanf("%d",&b);    printf("b = %d\n",b);    scanf("%d",&c);    printf("c = %d\n",c);    return 0;}
输出结果是:
1
a = 1
2
b = 2
3
c = 3
或者:
1 2 3
a = 1
b = 2
c = 3

从输出结果可以看出,不管是一个一个地输入:1(回车)2(回车)3(回车);还是三个数字一次性输入:1(空格)2(空格)3(回车),这两种输入方法的结果都是一样的。原因是从键盘输入的数据都会被依次存入缓冲区,不管是数字还是字符都会被当成数据存进去。但只有按回车,scanf 才会进去取数据,所取数据的个数取决于 scanf 中“输入参数”的个数。因此不在于怎么输入,可以存一个取一个,也可以一次性全存入进去,然后一个个取。

那么一次性全存进去,scanf 是如何取数据的呢?这就好比开闸放水,我们将水一次性全部放到闸里以后,开一次闸就放掉一点,开一次闸就放掉一点,直到放光了为止。开闸的动作就相当于调用一次 scanf,开闸的开关就是回车;每开一次闸放掉多少水,取决于 scanf 中“输入参数”的个数。

所以,输入的数据放在输入缓冲区中,先输入的排在最前面,后输入的依次往后排。如果 scanf 中“输入参数”的个数只有一个,那么我们调用一次 scanf 就把缓冲区中离出口最近的一个数据输出给 scanf,也就是把排在最前面的一个数据输出给 scanf。输出后,缓冲区中就没有这个数据了。

如果 scanf 中“输入参数”的个数为 n,那么就从排在最前面的开始,依次往后取 n 个数据输出给 scanf。没取完的仍旧放在缓冲区中,直到取用完毕为止。如果缓冲区中的数据全被取完了,但还有 scanf 要取数据,那就要再从键盘输入数据。
%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,c);    return 0;}
输出结果是:
1 5 6
a = 1,b = ,c = 5

在此程序中,原本希望的是将数字 1 赋给变量 a,将字符 '5' 赋给变量 b,将数字 6 赋给变量 c。但从输出结果可以看出,按一下回车,scanf 开始到缓冲区中取数据,因为“输入参数”有三个,所以 scanf 从缓冲区中取三个数据。数字 1 赋给变量 a,而因为变量 b 是 %c,所以前三种情况分别将空格、回车和 Tab 键赋给变量 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
a = 10您想继续吗(Y/N):

当我们输入“10”之后希望系统问:“您想继续吗(Y/N):”,若为“Y”就再重新输入一个值,然后输出,否则就跳出本循环体。但是执行的时候我们发现,刚按完“10”然后回车,直接就结束了,都不给我们输入“Y”和“N”的机会,这是为什么?

因为输入“10”然后回车,“10”赋给了a,但是回车遗留在了缓冲区,所以等下面又遇到“scanf("%c",&i);”的时候就直接把字符 '\n' 赋给变量 i 了(注意,按回车不是把回车符 '\r' 存到缓冲区,而是把换行符 '\n' 存进去了,因为按回车确实就是换行)。字符 '\n' 明显不等于字符 'Y',所以直接 break 跳出本层循环体。

那么该怎么办呢?方法有两个既然不想将字符'\n' 赋给变量 i,那么就先定义一个字符变量 ch,然后用 scanf 将字符 '\n' 取出来给变量 ch,这样就有机会输入“Y”或者“N”了;直接清空输入缓冲区。用scanf吸收回车
# include <stdio.h>int main(voID){    int a;    char i;    char ch;    while (1)    {        printf("请输入一个数字:");        scanf("%d",&ch);  //用scanf吸收回车        scanf("%c",&i);        if (('Y' == i) || ('y' == i))        {            ;        }        else        {            break;  // 跳出本层循环体        }    }    return 0;}
输出结果是:
请输入一个数字:10
a = 10您想继续吗,Y想,N不想:Y
请输入一个数字:5
a = 5您想继续吗,N不想:Y
请输入一个数字:333
a = 333您想继续吗,N不想:N

这时有人说,如果缓冲区前面排了三个字符'\x20'(空格),我都不需要,想先把它们取出来,那是不是要先定义三个变量呢?当然不是!存储不需要的垃圾字符只需要一个变量即可,因为它们都是垃圾,所以直接覆盖就行了。取一个后,再取一个就把第一个覆盖,再取一个就再覆盖。

但是在实际编程中,一般不会用 scanf 吸收回车,也不会用 scanf 给一个字符变量赋值,因为有更简单的方法,就是用 getchar()。getchar() 是专门从缓冲区读取一个字符的函数。它是“吸收回车专业户”,简单、方便、好用。
getchar()该函数的原型为:

# include <stdio.h>
int getchar(voID);

功能是从缓冲区中读取一个字符。这个函数非常简单,连参数都没有,非常好用。下面用 scanf 给字符变量赋值和吸收回车的程序用 getchar() 修改一下:
# include <stdio.h>int main(voID){    int a;    char ch;    while (1)    {        printf("请输入一个数字:");        scanf("%d",a);        printf("您想继续吗(Y/N):");        getchar();  /*用getchar吸收回车,简单、方便、好用,都不需要定义变量用来存储获取的回车符*/        ch = getchar();  //用getchar从缓冲区中读取一个字符赋给字符变量ch        if (('Y' == ch) || ('y' == ch))        {            ;        }        else        {            break;  // 跳出本层循环体        }    }    return 0;}
输出结果是:
请输入一个数字:10
a = 10您想继续吗(Y/N):y
请输入一个数字:5
a = 5您想继续吗(Y/N):y
请输入一个数字:333
a = 333您想继续吗(Y/N):n

在程序中,“ch=getchar();”这句之前我们先用 getchar() 清空缓冲区,然后重新从键盘输入一个字符。同样,必须按回车 getchar() 才会进去取这个字符。这时候需要注意的是,同 scanf 一样,按的这个回车也会被遗留在缓冲区中,大家要注意。

这时有人会说,如果前面有多个 scanf 给 int 型变量赋值,那么每个 scanf 都会遗留一个回车,那这时是不是有几个 scanf 就要用几个 getchar() 呢?

回答是“不需要”,仍然只需要一个 getchar()!前面说过,当 scanf 用 %d 取缓冲区数据的时候,如果遇到空格、回车或 Tab 键就跳过去。不仅如此,这些被跳过去的空白符都被释放了。所以假如前面有三个 scanf 给 int 型变量赋值,那么第一个 scanf 输入回车后把回车遗留在了缓冲区,而第二个 scanf 取值时会越过第一个 scanf 遗留在缓冲区中的回车,那么这个回车就会从缓冲区中释放。但第二个 scanf 取完值后也在缓冲区中留下了一个回车,而当第三个 scanf 到缓冲区中取值时会跳过第二个 scanf 遗留的回车,这个回车同样也会从缓冲区中释放,所以归根结底最后缓冲区中只有一个回车,也就是说,缓冲区中永远不可能遗留多个回车。

下面来写一个程序验证一下:
# include <stdio.h>int main(voID){    int a,c,d;    char ch;    printf("请输入第一个数:");    scanf("%d",&a);    printf("请输入第二个数:");    scanf("%d",&b);    printf("请输入第三个数:");    scanf("%d",&c);    printf("您想继续吗(Y/N):");    getchar();  //只需要用一个getchar吸收回车    ch = getchar();  //用getchar获取一个字符赋给ch    if (('Y' == ch) || ('y' == ch))    {        printf("请输入密码:");        scanf("%d",&d);        printf("恭喜成功获得密码%d\n",d);    }    return 0;}
输出结果是:
请输入第一个数:1
请输入第二个数:2
请输入第三个数:3
您想继续吗(Y/N):y
请输入密码:5678
恭喜成功获得密码5678

由此我们知道,当用 %d 获取输入流中的数据的时候,如果遇到字符(空格、回车、Tab 除外),则直接从输入流中退出来,什么都不取。但如果是用 %c 获取,那么任何数据都会被当作一个字符。所以如果你要从输入流中取一个字符,但在之前使用过 scanf,那么此时就必须要先用 getchar() 吸收回车。否则取到的将不是你想要的字符,而是 scanf 遗留在输入流中的回车。如果你要从输入流中取的不是字符,那就不需要用 getchar() 吸收回车了。

以上详细分析了什么时候需要吸收回车,什么时候不需要。但实际编程中,程序往往很长,我们很难预测到下一次到缓冲区中取数据的是 %d 还是 %c 或者是 gets()、fgets()。所以为了避免忘记吸收回车或耗费精力考虑回车的问题,习惯上 scanf 后面都加上 getchar()。
fflush(stdin)前面介绍了使用 getchar() 吸收回车的方法,除此之外还有一个更强大、更直接的方法,就是直接将输入缓冲区全部清空。

清空缓冲区只需加一句 fflush(stdin) 即可。fflush 是包含在文件 stdio.h 中的函数。stdin 是“标准输入”的意思。std 即 standard(标准),in 即 input(输入),合起来就是标准输入。fflush(stdin) 的功能是:清空输入缓冲区。下面将前面的一个程序修改一下:
# include <stdio.h>int main(voID){    int a;    char i;    while (1)    {        printf("请输入一个数字:");        scanf("%d",&a);  //因为读取的是数字,所以不需要清空缓冲区        printf("a = %d\n",a);        printf("您想继续吗,N不想:");        fflush(stdin);        scanf("%c",&i);        if ('Y'==i || 'y'==i)        {            ;        }        else        {            break;  // 跳出本层循环体        }    }    return 0;}
输出结果是:
请输入一个数字:10safa
a = 10
您想继续吗,N不想:Y
请输入一个数字:58jlkj\*&**^
a = 58
您想继续吗,N不想:N

fflush 一般用于清除用户前面遗留的垃圾数据,提高代码的健壮性。因为如果是自己编程的话,一般都会按要求输入。但对于用户而言,难免会有一些误 *** 作,多输入了一些其他没有用的字符,如果程序中不对此进行处理的话可能会导致程序瘫痪。所以编程时一定要考虑到各种情况,提高代码的健壮性和容错性。使用 fflush 就可以将用户输入的垃圾数据全部清除。

但是 fflush 有一个问题,就是可移植性。并不是所有的编译器都支持 fflush,比如 gcc 就不支持。那么此时怎么办?还是用 getchar()。
getchar()的高级用法

while (getchar() != '\n');

这种用法其实在前面也使用过,它可以完全代替 fflush(stdion) 来清空缓冲区。不管用户输入多少个没用的字符,他最后都得按回车,而且只能按一次。只要他按回车那么回车之前的字符就都会被 getchar() 取出来。只要 getchar() 取出来的不是回车 ('\n') 那么就会一直取,直到将用户输入的垃圾字符全部取完为止。
总结

以上是内存溢出为你收集整理的清空输入缓冲器,C语言清空输入缓冲区完全攻略全部内容,希望文章能够帮你解决清空输入缓冲器,C语言清空输入缓冲区完全攻略所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1235643.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-06
下一篇 2022-06-06

发表评论

登录后才能评论

评论列表(0条)

保存