为什么scanf函数出现跳过输入?(Why does scanf appear to skip in

2019-06-25 07:49发布

我感到困惑下面的程序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。

我知道这里的整数值由程序预期,但我想知道当角色到位整数输入会发生什么? 什么是所有上述情况发生的原因是什么?

Answer 1:

当你输入a ,然后cin >> i无法读取它,因为类型iint到一个字符不能读取。 这意味着, 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";
}


Answer 2:

如果scanf认为是不匹配的转换说明在输入流中的字符,它停止转换和离开输入流中的违规性质。

有一对夫妇的方式来解决这个问题。 一个是读一切为文本(使用scanf%s%[转换说明或fgets ),然后使用atoistrtol做转换(我的首选方法)。

或者,你可以检查返回值scanf ; 它会显示成功转换次数。 因此,如果scanf("%d", &i); 等于0,那么你知道有输入流中的不良性格。 你可以使用它getchar()然后再试一次。



Answer 3:

你也休想用户输入有效的东西。 最好的做法是读取输入的字符串,并试图将其转换为整数 。 如果输入的不是整数 ,你可以给一个错误信息给用户。



Answer 4:

问题是,当你进入一个输入是期望的类型不(通过指定%d的scanf函数,以及int类型cin>>i; ,InputStream的不先进,导致这两个操作试图提取同类型由完全相同的不正确的输入数据(和失败一样好这个时间太长左右)的,因此你将永远不会要求其他输入。

为了确保不会发生这种情况,你将需要检查这两个操作的返回值(阅读了每个报告错误手册)。 如果错误确实发生 (因为当你输入一个字符),您将需要清除错误,消耗无效输入,然后再试一次。 我发现它在C ++更好的用来阅读一整行std::gtline()而不是int甚至std::string从疗法用户歌厅输入时交互,让你进入你经历过这种“无限”循环。



Answer 5:

您忽略返回值。 看看手册上说有关scanf(3)

RETURN VALUE
These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.

它没有匹配的整数。



Answer 6:

您可以检查返回值scanf ,以确定是否为整数正确分析(投资回报率应= 1)。 如果失败,你必须选择:要么将错误通知用户和终止,或通过读取下一个标记与恢复scanf("%s" ...)或许是一个警告。



Answer 7:

对于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;
    }


文章来源: Why does scanf appear to skip input?
标签: c++ c io scanf cout