什么时候以及为什么需要在C ++中使用cin.ignore()?


73

我用C ++写了一个非常基本的程序,要求用户输入一个数字,然后输入一个字符串。令我惊讶的是,在运行该程序时,它从未停止过询问字符串。它只是跳过了它。在对StackOverflow进行一些阅读之后,我发现我需要添加一行内容:

cin.ignore(256, '\n');

在获取字符串输入的行之前。添加该功能可以解决问题并使程序正常运行。我的问题是,为什么C ++需要这一cin.ignore()行?如何预测何时需要使用cin.ignore()

这是我编写的程序:

#include <iostream>
#include <string>

using namespace std;

int main()
{
    double num;
    string mystr;

    cout << "Please enter a number: " << "\n";
    cin >> num;
    cout << "Your number is: " << num << "\n";
    cin.ignore(256, '\n'); // Why do I need this line?
    cout << "Please enter your name: \n";
    getline (cin, mystr);
    cout << "So your name is " << mystr << "?\n";
    cout << "Have a nice day. \n";

}

您所读的答案没有解释吗?为什么不在评论中要求澄清?现在,我们在该主题上有另一个副本。叹。
Lightness Races in Orbit

3
@LightnessRacesinOrbit投票以重复方式关闭吗?
MM

@LightnessRacesinOrbit很抱歉重复这个问题。
Raddicus 2014年

@MattMcNabb:是的,但是cba可以找到它。如果我不必的话,那会很好。
Lightness Races in Orbit

好的,所以当我不加忽略地运行您的代码时,它会跳过第二个cin >>,大概是因为endl仍在流中。但是,当我使用ignore运行您的代码时,如果我键入数字字符以外的任何内容,它仍会跳过第二个cin >>。如果我输入两个忽略,它将在第二个cin >>之前等待第三个输入。谁能解释一下?
凯尔·德莱尼

Answers:


73

正是名称所隐含的含义。

它不会“丢弃”您不需要的东西,而是会忽略您在调用它时指定的字符数,直到指定为断点的char为止。

它适用于输入和输出缓冲区。

从本质上讲,对于std::cingetline调用之前您使用ignore的语句,因为当用户使用来输入内容时std::cin,他们会按Enter键,并且'\n'char进入cin缓冲区。然后,如果使用getline,它将获取换行符char而不是所需的字符串。因此,您做一个std::cin.ignore(1000,'\n')并且那应该清除缓冲区直到您想要的字符串。(将1000放在此处可跳过指定断点(在本例中为\ n换行符)之前的特定数量的字符。)


1
谢谢。问为什么C ++会像您所描述的那样在cin缓冲区中获取'\ n'字符的问题,这会太多吗?这是语言的缺陷吗?还是出于某种目的有意地这样做了?
Raddicus

6
不完全是一个缺陷,更像是精度。从技术上讲,如果未读入换行符,则它不会读取所有内容。输入缓冲区会忽略空格,因此当您从std :: cin读取信息时,换行并不重要。.但是当您谈论getline时,它将使整行到达换行符,并且当换行char为getline函数看到的第一件事就是它得到的全部。
savageWays 2014年

20
您需要使用numeric_limits<streamsize>::max()忽略所有的字符,而不是1000
尼尔·柯克

1
根据.ignore()标准,“提取并丢弃字符”,所以我认为这很接近丢弃cplusplus.com/reference/istream/istream/ignore
csguy

30

您正在以错误的方式思考。您每次都在按逻辑步骤进行思考cin或被getline使用。例如 首先要求一个数字,然后要求一个名称。那是错误的思考方式cin。因此,您会遇到竞争状况,因为您假设每次要求输入时流都是干净的。

如果仅出于输入目的编写程序,则会发现问题:

void main(void)
{
    double num;
    string mystr;

    cin >> num;
    getline(cin, mystr);

    cout << "num=" << num << ",mystr=\'" << mystr << "\'" << endl;
}

在上面,您在思考“首先获得一个数字”。因此,您123按Enter键,输出将是num=123,mystr=''。这是为什么?这是因为在流中您拥有,123\n并且在流中仍将123解析为num变量\ngetline默认情况下,读取功能文档时,它会istream一直查找到,直到\n遇到。在此示例中,由于\n处于流中,因此看起来像“跳过”了它,但是它正常工作。

为了使以上功能正常工作,您必须输入123Hello World才能正确输出num=123,mystr='Hello World'。那,或者您cin.ignorecin和之间放置一个,getline以便将其分解为您期望的逻辑步骤。

这就是为什么您需要该ignore命令的原因。因为您是在逻辑步骤而不是在流形式中考虑它,所以您遇到了竞争状况。

再举一个学校里常见的代码示例:

void main(void)
{
    int age;
    string firstName;
    string lastName;

    cout << "First name: ";
    cin >> firstName;

    cout << "Last name: ";
    cin >> lastName;

    cout << "Age: ";
    cin >> age;

    cout << "Hello " << firstName << " " << lastName << "! You are " << age << " years old!" << endl;
}

以上似乎是合乎逻辑的步骤。首先询问名字,姓氏,然后询问年龄。因此,如果您确实John输入了,然后Doe输入,然后19输入,则应用程序将执行每个逻辑步骤。如果您在“信息流”中想到它,则只需输入John Doe 19“名字:”问题,它也可以正常工作,并且看起来可以跳过其余的问题。为了使以上内容能够按逻辑步骤工作,您需要ignore为每个逻辑上的问题中断提供剩余的内容。

只需记住您的程序输入是从“流”中读取的,而不是逻辑步骤。每次调用时cin,都会从流中读取它。如果用户输入了错误的输入,则会创建一个有问题的应用程序。例如,如果您输入了cin >> double预期的a字符,则应用程序将产生看似奇怪的输出。


15

简短答案

为什么?因为在输入流中仍然留有空格(回车,制表符,空格,换行符)。

什么时候?当您使用某些功能时,它们本身并不会忽略前导空格。Cin默认情况下会忽略并删除前导空格,但是getline不会独自忽略前导空格。

现在详细回答。

您在控制台中输入的所有内容都将从标准流stdin中读取。当您输入内容时,假设您输入的是256,然后按Enter,则流的内容变为256\n。现在,cin拾取256并将其从流中删除,并且\n仍然保留在流中。接下来,当您输入名称时,假设Raddicus流的新内容为\nRaddicus

现在来了。当您尝试使用getline读取行时,如果未提供任何分隔符作为第三个参数,则getline默认情况下会读取直到换行符,然后从流中删除换行符。因此,在调用新行时,getline\n从流中读取并丢弃该流,并导致在mystr中读取一个空字符串,该字符串看起来像被跳过了getline(但并非如此),因为流中已经存在换行符,getline将不会提示输入它已经阅读了应该阅读的内容。

现在,cin.ignore在这里有何帮助?

根据cplusplus.com的忽略文档摘录-

istream&忽略(streamsize n = 1,int delim = EOF);

从输入序列中提取字符并丢弃它们,直到提取出n个字符或一个等于delim的比较字符为止。

如果到达文件末尾,该函数还将停止提取字符。如果过早达到此目的(在提取n个字符或找到delim之前),该函数将设置eofbit标志。

因此,会cin.ignore(256, '\n');忽略前256个字符或所有字符,直到遇到定界符为止(这里是\ n),以先到者为准(这里\ n是第一个字符,因此它会忽略直到遇到\ n为止)。

仅供参考,如果您不完全知道要跳过多少个字符,而您的唯一目的是清除流以准备使用getline或cin读取字符串,则应使用 cin.ignore(numeric_limits<streamsize>::max(),'\n')

快速说明:它将忽略等于流的最大大小的字符,或者直到遇到'\ n'为止,以先发生的情况为准。


6

当您想手动从输入流中丢弃特定数量的字符时。

一个非常常见的用例是使用它来安全地忽略换行符,因为cin有时会留下换行符,您将不得不移至新的输入行。

长话短说,它为您提供处理流输入时的灵活性。


感谢您的回复。在cin之后,有没有更有效的方法来使用getline()?我是否应该计划在每行之间先插入cin,然后再插入getline()?这对我来说似乎很愚蠢。当您比我拥有更多的C ++经验时,也许更有意义。
Raddicus

1
@Raddicus这确实很愚蠢,尤其是因为我们知道'\n'在IOStreams中将其解析为空白,并且IOStreams提供了std::ws丢弃前导空白的操纵器。您要做的就是std::getline(std::cin >> ws, mystr)忽略所有空格和换行符。
0x499602D2 2014年

1
@Raddicus:我建议你避免混合operator>>,并getline在同流。例如,with cin,只能使用getline。如果您需要非字符串数据,请解析getline使用istringstream或获得的字符串stoi/stod/etc...
Benjamin Lindley 2014年

而不是“有时”,确切描述何时何地
MM

@ 0x499602D2在这种情况下是个好主意,但在其他情况下空白行很重要(例如,使用默认值输入),因此仍然需要理解该问题
MM

1

忽略功能用于跳过(丢弃/丢弃)输入流中的字符。忽略文件与文件istream关联。考虑以下函数:cin.ignore(120,'/ n'); 特定功能将跳过下一个120个输入字符,或者跳过这些字符,直到读取换行符为止。


0

正如许多其他用户所指出的那样。这是因为可能存在空格或换行符。

考虑下面的代码,它从给定的字符串中删除所有重复的字符。

#include <bits/stdc++.h>
using namespace std;

int main() {
    int t;
    cin>>t;
    cin.ignore(); //Notice that this cin.ignore() is really crucial for any extra whitespace or newline character
    while(t--){
        vector<int> v(256,0);
        string s;
        getline(cin,s);
        string s2;
        for(int i=0;i<s.size();i++){
            if (v[s[i]]) continue;
            else{
                s2.push_back(s[i]);
                v[s[i]]++;
            }
        }
        cout<<s2<<endl;
    }
    return 0;
}

因此,您得到的一点是它将忽略那些不需要的输入并完成工作。


-3

在cin >>语句之后,最好在c ++中使用scanf(“%[^ \ n]”,str)而不是cin.ignore()。为此,首先必须包含<cstdio>标头。


1
不对。scanf<cstdio>标头的一部分,不是从用户获取输入的惯用C ++方法。
丹尼尔(Daniel)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.