我感到困惑下面的程序scanf函数的行为。 scanf的出现输入一次,然后再次再输入,直到字符流被打印出来。
下面的C程序
#include<stdio.h>
int main()
{
int i, j=0;
do
{
++j;
scanf("%d", &i);
printf("\n\n%d %d\n\n", i, j);
}
while((i!=8) && (j<10));
printf("\nJ = %d\n", j);
return 0;
}
在这里,直到我正在输入任何整数规划工作完全正常,但是当一个字符输入那张打印我的最后inputed值和永不停止(直到j为10时退出循环)为scanf函数采取下一个输入。
output::
1 <-----1st input
1 1
2 <---- 2nd input
2 2
a <---- character input
2 3
2 4
2 5
2 6
2 7
2 8
2 9
2 10
J = 10
同样的事情在C ++中也发生。
#include<iostream>
using namespace std;
int main()
{
int i, j=0;
do
{
++j;
cin>>i;
cout<<i<<" "<<j<<"\n";
}
while((i!=8) && (j<10));
cout<<"\nj = "<<j<<"\n";
}
output of c++ program ::
1 <-----1st input
1 1
2 <-----2nd input
2 2
a <------ character input
0 3
0 4
0 5
0 6
0 7
0 8
0 9
0 10
j = 10
只在C ++中的变化是,正在打印的代替最后一个值0。
我知道这里的整数值由程序预期,但我想知道当角色到位整数输入会发生什么? 什么是所有上述情况发生的原因是什么?
当你输入a
,然后cin >> i
无法读取它,因为类型i
是int
到一个字符不能读取。 这意味着, a
仍然在流中永远。
现在为什么i
打印0
是一个不同的故事。 实际上,它可以打印任何东西。 的内容i
没有定义一次读取尝试失败。 类似的事情发生与scanf
为好。
正确的方法是这样写:
do
{
++j;
if (!(cin>>i))
{
//handle error, maybe you want to break the loop here?
}
cout<<i<<" "<<j<<"\n";
}
while((i!=8) && (j<10));
或者干脆这个(如果你想,如果发生错误,退出循环):
int i = 0, j = 0;
while((i!=8) && (j<10) && ( cin >> i) )
{
++j;
cout<<i<<" "<<j<<"\n";
}
如果scanf
认为是不匹配的转换说明在输入流中的字符,它停止转换和离开输入流中的违规性质。
有一对夫妇的方式来解决这个问题。 一个是读一切为文本(使用scanf
用%s
或%[
转换说明或fgets
),然后使用atoi
或strtol
做转换(我的首选方法)。
或者,你可以检查返回值scanf
; 它会显示成功转换次数。 因此,如果scanf("%d", &i);
等于0,那么你知道有输入流中的不良性格。 你可以使用它getchar()
然后再试一次。
你也休想用户输入有效的东西。 最好的做法是读取输入的字符串,并试图将其转换为整数 。 如果输入的不是整数 ,你可以给一个错误信息给用户。
问题是,当你进入一个输入是期望的类型不(通过指定%d
的scanf函数,以及int
类型cin>>i;
,InputStream的不先进,导致这两个操作试图提取同类型由完全相同的不正确的输入数据(和失败一样好这个时间太长左右)的,因此你将永远不会要求其他输入。
为了确保不会发生这种情况,你将需要检查这两个操作的返回值(阅读了每个报告错误手册)。 如果错误确实发生了 (因为当你输入一个字符),您将需要清除错误,消耗无效输入,然后再试一次。 我发现它在C ++更好的用来阅读一整行std::gtline()
而不是int
甚至std::string
从疗法用户歌厅输入时交互,让你进入你经历过这种“无限”循环。
您可以检查返回值scanf
,以确定是否为整数正确分析(投资回报率应= 1)。 如果失败,你必须选择:要么将错误通知用户和终止,或通过读取下一个标记与恢复scanf("%s" ...)
或许是一个警告。
对于scanf
,则需要检查它的返回值,看看是否对输入的转换工作。 scanf
将返回成功扫描元件的数量。 如果转换没有工作,它会独自离开输入,你可以尝试以不同的方式进行扫描,或者只是报告错误。 例如:
if (scanf("%d", &i) != 1) {
char buf[512];
fgets(buf, sizeof(buf), stdin);
printf("error in line %d: got %s", j, buf);
return 0;
}
在你的程序中,由于输入被单独留在家中,你的循环重复尝试读取相同的输入。
在C ++中,检查用于使用失败fail
的方法,但是输入流故障状态是粘性的。 因此,它不会让你进一步的扫描,而无需清除错误状态。
std::cin >> i;
if (std::cin.fail()) {
std::string buf;
std::cin.clear();
std::getline(cin, buf);
std::cout
<< "error in line " << j
<< ": got " << buf
<< std::endl;
return 0;
}
在你的程序,因为你永远不清除故障状态,循环重复使用cin
处于故障状态,所以它只是报告故障没有做任何事情。
在这两种情况下,你可能会发现更容易或更可靠与输入工作,如果你在输入行先读,然后尝试解析输入线。 在伪代码:
while read_a_line succeeds
parse_a_line
在C中,捕捉到读线是,如果它比你的缓冲时间越长,你将需要检查的是和连接多个fgets
调用结果在一起,形成线。 而且,解析一条线,你可以使用sscanf
,这是类似scanf
但它只是对字符串。
if (sscanf(buf, "%d", &i) != 1) {
printf("error in line %d: got %s", j, buf);
return 0;
}
在C ++中,用于格式化输入的快速低水平的解析,我也比较喜欢sscanf
。 但是,如果你想使用流的方法,可以将转换string
缓冲区为istringstream
扫描输入。
std::getline(cin, buf);
if (std::cin.fail()) {
break;
}
std::istringstream buf_in(buf);
buf_in >> i;
if (buf_in.fail()) {
std::cout << "error in line " << j
<< ": got " << buf
<< std::endl;
return 0;
}