| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 1189 人关注过本帖
标题:[讨论]FAQ新增条目建议 输入缓冲区垃圾及处理
只看楼主 加入收藏
lg_mic
Rank: 1
等 级:新手上路
帖 子:55
专家分:0
注 册:2007-9-18
收藏
 问题点数:0 回复次数:8 
[讨论]FAQ新增条目建议 输入缓冲区垃圾及处理
情况1
首先让我们来看看下面的程序
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int a,b;
char c1,c2;
scanf("%d %d",&a,&b);
scanf("%c %c",&c1,&c2);
printf("c1='%c',c2='%c'\n",c1,c2);
system("pause");
return 0;
}
输入两个整数,并赋给a与b,然后输入两个字符给c1与c2,输出c1与c2。这个程序看上去是没有错误的。实际操作一下看看。
输入:
12 34
a b
输出:
c1='
',c2=' '
这很明显和我们所期望结果的不同,它的错误是在输入12,34并回车后,程序将输入缓冲区中的\n直接读入了,结果造成c1='\n'。下面提供两种解决方法。
第一种:死了都要C提供的方法
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a,b;
char c1,c2;
scanf("%d %d",&a,&b);
getchar();
scanf("%c %c",&c1,&c2);
printf("c1='%c',c2='%c'\n",c1,c2);
system("pause");
return 0;
}
他在第二个scanf前加了一行getchar();经测试可以显示c1=a,c2=b了。那么这个方法的原理是什么呢,用错误数据来测试一下。
输入:
12 34a
b c
输出:
c1='
',c2='b'
换一个输入试试
输入:
12,34abcd(输入两个或两个以上的字符)
(输入c1和c2的过程直接被程序跳过了)
输出:
c1='b',c2='c'
从以上的输入和输出可以看出,getchar()似乎并没有将输入缓冲区清空,只是向后跳了一个字符,即c1等于"12,34"后第二个被输入的字符。所以正常输入时,"12,34"后的"\n"被跳过了,使得c1=a,c2=b。当然,平常输入的时候也不会有人白痴到在"12,34"后再输入其他的字符,我这样做的原因只是想要说明在我的理解中用getchar()是怎样达成正确输入的效果的。

再来看看第二种方法:百年不亮提供的方法
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int a,b;
char c1,c2;
scanf("%d %d",&a,&b);
while(getchar()!='\n');
scanf("%c %c",&c1,&c2);
printf("c1='%c',c2='%c'\n",c1,c2);
system("pause");
return 0;
}
这个方法在第二个scanf前加了一行while(getchar()!='\n');经测试没有问题,那么同样输入错误数据看看。
输入:
12 34a
b c
输出:
c1='b',c2='c'
输入:
12 34abcdef
b f
输出:
c1='b',c2='f'
可以看出,在用while(getchar()!='\n');的情况下"12,34"后的第一个"\n"前的所有字符都被清空了。这两种方法都可以使用,不过比较下来应该是第二种方法比较实用。当然,谁有更好的方法也请提出来,知识是不嫌多的。


情况2
再来看另一个程序
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
int main(void)
{
int result,n1,n2,n,flag;
char c;
do{
flag=1;
printf("\nChoose an operator:1.+ 2.- 3.* 4./:\n");
scanf("%d",&n);
printf("Enter two numbers:\n");
scanf("%d %d",&n1,&n2);
switch(n)
{
case 1: result=n1+n2; break;
case 2: result=n1-n2; break;
case 3: result=n1*n2; break;
case 4: result=n1/n2; break;
default: printf("Bad operator!\n");
flag=0;
}
if(flag)
printf("The result is %d\n",result);
printf("Would you like to do another problem?(Y/N):\n");
c=getch();
}while(c=='y'||c=='Y');
return 0;
}
这是一个整数计算器程序,首先选择要用那种算法,然后输入两个数字,将结果输出后询问是否要继续运行程序,输入"y"或"Y"后程序从头运行,输入任意键退出。如果在选择算法的时候输入了错误的数字,则输出"Bad operator!"并询问是否要继续运行程序。这个程序本身没有什么问题,不过在一些错误的输入下就会有问题了,运行看看。
Choose an operator:1.+ 2.- 3.* 4./:
(输入1.1)
Enter two numbers:
The result is 2293730
Would you like to do another problem?(Y/N):
(输入y)
Choose an operator:1.+ 2.- 3.* 4./:
Enter two numbers:
The result is 2293730
Would you like to do another problem?(Y/N):

可以看到,输入两数的过程被程序跳过了,然后当继续运行的时候,程序就开始反复输出之前的内容。造成这种情况的原因是scanf("%d",&n);只能接受整数,当输入非整数时,输入的数据就会遗留在输入缓冲区中。而由于缓冲区中有数据,scanf函数就不会等用户输入,直接从缓冲区中读取数据,结果读取到的还是无法接受的数据,这个数据再次被留在缓冲区中,就这样反复不止。那么这种情况应该如何解决呢?看看josen0205提供的方法
#include<stdio.h>
#include<conio.h>
int main()
{
int result,n1,n2,n;
char c;
while(1)
{
printf("\nChoose an operator:1.+ 2.- 3.* 4./:");
if(scanf("%d",&n)!=1||n<1||n>4)
{
printf("Bad operator!\n");
fflush(stdin);
continue;
}

printf("Enter two numbers:");
scanf("%d %d",&n1,&n2);
switch(n)
{
case 1: result=n1+n2; break;
case 2: result=n1-n2; break;
case 3: result=n1*n2; break;
case 4: result=n1/n2; break;
default :
break;
}
printf("The result is %d\n",result);
printf("Would you like to do another problem?(Y/N):");
c=getch();
putchar(c);
if(c=='n'||c=='N')
break;

}
getch();
return 0;
}

他的程序中用if(scanf("%d",&n)!=1||n<1||n>4)来判断输入的数据是否在(1,4)的区间中,如果输入数据不在该区间中则输出"Bad operator!",用fflush(stdin);清空缓冲区并重新要求输入数据。用他的方法可以很好的解决类似问题。

最后做一下总结:
1、在连续有多个scanf语句时,最好将%c格式放在最前面,或使用getchar();或while(getchar()!='\n');类的语句跳过"\n"
2、在上述的第二个程序的那种情况下,最好使用fflush(stdin);来清空输入缓冲区。

以上就是我所遇到的两种情况,如果还有其他的情况,请大家补充。

ok.我把输入的部分都改成了空格分隔。

另外,在帖子“缓冲流问题,输入一个数后,下一个不能再读取(printf,scanf的问题)”http://bbs.bc-cn.net/viewthread.php?tid=171684又学到了一种解决方法:

在连续有多个scanf语句时,如果%c在后面的话,可以遵照以下方法

#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int a,b;
char c1,c2;
scanf("%d %d",&a,&b);
scanf(" %c %c",&c1,&c2);
printf("c1='%c',c2='%c'\n",c1,c2);
system("pause");
return 0;
}

在%c前加一个空格,它的作用是,“scanf的控制字符串中加空格可以过滤掉空白字符(如回车,制表)

scanf(" %c",&a); %c前的空格可以过滤掉上次敲回车后留下的'\n',从第一个非空白字符开始读。

建议看《c primer plus》第八章 字符输入/输出和输入确认”(引用自百年不亮的解释)





特别感谢死了都要C百年不亮,josen0205和felicia!

[此贴子已经被作者于2007-9-22 17:45:52编辑过]

搜索更多相关主题的帖子: FAQ 条目 缓冲区 垃圾 
2007-09-21 01:39
死了都要C
Rank: 4
来 自:四川成都
等 级:贵宾
威 望:13
帖 子:1582
专家分:116
注 册:2006-12-7
收藏
得分:0 

我觉得很好```所以我们以后不仅要注意键盘缓冲``还要注意自己的输入```




女施主``我给你``送茶来了```师太``你就从了老衲吧``
代码本天成~~~妙头偶得之```
2007-09-21 09:54
felicia
Rank: 1
等 级:新手上路
帖 子:28
专家分:0
注 册:2007-9-21
收藏
得分:0 
楼主举的例子应该把格式串中的逗号全部改成空格

2007-09-21 09:57
死了都要C
Rank: 4
来 自:四川成都
等 级:贵宾
威 望:13
帖 子:1582
专家分:116
注 册:2006-12-7
收藏
得分:0 
LZ的scanf语句:

scanf("%d,%d",&a,&b);

scanf("%c,%c",&c1,&c2);

scanf("%d,%d",&n1,&n2);


那么我们在输入的时候就要按照里面的格式输入``必须用逗号阁开```
但是OJ和命令行里的输入我们一般都是用的空格``(多谢LS的提醒与批评)``应该把它们改一下:

scanf("%d %d",&a,&b);

scanf("%c %c",&c1,&c2);

scanf("%d %d",&n1,&n2);




[此贴子已经被作者于2007-9-21 22:52:13编辑过]


女施主``我给你``送茶来了```师太``你就从了老衲吧``
代码本天成~~~妙头偶得之```
2007-09-21 10:06
sunyuantz
Rank: 1
等 级:新手上路
威 望:1
帖 子:407
专家分:0
注 册:2006-3-20
收藏
得分:0 
我想问一下
printf("0000000\n");
getchar();
中getchar()接受的到底是什么?是printf的返回值吗?

我不是名人,所以不要签名。等哪天我成名人了......你都认识我了还要签名干嘛!
2007-09-21 19:11
百年不亮
Rank: 3Rank: 3
等 级:新手上路
威 望:8
帖 子:789
专家分:0
注 册:2006-4-14
收藏
得分:0 
5楼你这个问题太外行了。

一个是输出一个是输入,stdin和stdout是两个设备文件,以文件模式访问,分别读写连个文件是不会相互干扰的。
getchar()会去读输入缓冲区,至于会读到什么看当时的环境。

=====================================================================================================
还有3楼的建议不错,scanf中的‘,’最好换成空格,如果不提示用户输入格式,看过源代码的才知道用空格分隔。
scanf("%d,%d",&a,&b); 改为 :scanf("%d %d",&a,&b);

或者给提示:printf("输入两个整数,逗号分隔:");scanf("%d,%d",&a,&b);

======================================================================================================

正好中午有个缓冲区问题的帖子,你可以看下5楼8楼的回复,加入到1楼的文章中。
http://bbs.bc-cn.net/viewthread.php?tid=171684

======================================================================================================

我看完都花了不少时间更何况查资料后写出来 ,楼主辛苦了!!

[此贴子已经被作者于2007-9-21 20:04:56编辑过]

2007-09-21 20:03
sunyuantz
Rank: 1
等 级:新手上路
威 望:1
帖 子:407
专家分:0
注 册:2006-3-20
收藏
得分:0 

以前重来没注意这个,每次只知道老师让我们这样用,不过谢谢斑竹了,楼主讲的也详细,学习了


我不是名人,所以不要签名。等哪天我成名人了......你都认识我了还要签名干嘛!
2007-09-21 20:12
我是菜鸟哦
Rank: 6Rank: 6
等 级:贵宾
威 望:22
帖 子:921
专家分:209
注 册:2007-5-4
收藏
得分:0 
顶一个,好!

偶是菜鸟鸟偶惧WHO?!!!!
2007-09-21 22:03
交流者
Rank: 1
等 级:新手上路
帖 子:21
专家分:0
注 册:2007-9-5
收藏
得分:0 
没什么好说的,ding
2007-09-21 22:51
快速回复:[讨论]FAQ新增条目建议 输入缓冲区垃圾及处理
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.018407 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved