何时以及为啥需要在 C++ 中使用 cin.ignore()?

Posted

技术标签:

【中文标题】何时以及为啥需要在 C++ 中使用 cin.ignore()?【英文标题】:When and why do I need to use cin.ignore() in C++?何时以及为什么需要在 C++ 中使用 cin.ignore()? 【发布时间】:2014-10-17 23:34:26 【问题描述】:

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

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";


【问题讨论】:

这不是您阅读的答案中的解释吗?为什么不在评论中要求澄清?现在我们在这个主题上有另一个副本。叹息。 @LightnessRacesinOrbit 投票以重复关闭? @LightnessRacesinOrbit 抱歉重复了这个问题。 @MattMcNabb:是的,但是 cba 可以找到它。如果我不必这样做会很好。 好的,所以当我运行您的代码而不忽略时,它会跳过第二个 cin>>,大概是因为 endl 仍在流中。但是,当我使用 ignore 运行您的代码时,如果我键入数字字符以外的任何内容,它仍然会跳过第二个 cin>>。如果我输入两个忽略,它会在第二个 cin>> 之前等待第三个输入。谁能解释一下? 【参考方案1】:

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

一个非常常见的用例是使用它来安全地忽略换行符,因为 cin 有时会留下换行符,您必须重复这些换行符才能进入下一行输入。

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

【讨论】:

感谢您的回复。有没有更有效的方法在 cin 之后使用 getline() ?我是否应该计划在每行之间添加一条忽略线,其中一个 cin 后跟一个 getline()?这对我来说似乎很愚蠢。当你比我有更多的 C++ 经验时,也许它更有意义。 @Raddicus 这真的很愚蠢,特别是因为我们知道'\n' 在 IOStreams 中被解析为空格,而 IOStreams 提供了 std::ws 操纵器,它丢弃了前导空格。您所要做的就是std::getline(std::cin &gt;&gt; ws, mystr) 忽略所有空格和换行符。 @Raddicus:我建议您避免在同一流中混合operator&gt;&gt;getline。例如,对于cin,只能使用getline。如果您需要非字符串数据,则使用istringstreamstoi/stod/etc... 解析从getline 获得的字符串 而不是“有时”,准确描述时间和地点 @0x499602D2 在这种情况下是个好主意,但在其他情况下,空行很重要(例如使用默认值输入),因此仍然需要了解问题【参考方案2】:

忽略正是顾名思义。

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

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

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

【讨论】:

谢谢。像您描述的那样,问为什么 C++ 会在 cin 缓冲区中获取 '\n' 字符的问题是不是太过分了?这是语言的缺陷吗?还是故意这样做是因为它有某种目的? 不完全是缺陷,更像是精确度。从技术上讲,如果换行符没有被读入,那么它就不会读取所有内容。输入缓冲区会忽略空格,因此当您从 std::cin 读取信息时,换行符无关紧要。但是当您谈论 getline 时,它​​会将整行获取到换行符,并且当换行符是getline 函数首先看到的就是它所得到的一切。 您需要使用numeric_limits&lt;streamsize&gt;::max() 忽略所有字符,而不是1000。 按照标准,.ignore()“提取和丢弃字符”,所以我认为这非常接近于扔掉cplusplus.com/reference/istream/istream/ignore【参考方案3】:

忽略函数用于跳过(丢弃/丢弃)输入流中的字符。忽略文件与文件 istream 相关联。 考虑下面的函数 例如:cin.ignore(120,'/n'); 特定函数跳过下一个 120 个输入字符或跳过字符直到读取换行符。

【讨论】:

【参考方案4】:

你想错了。每次使用 cingetline 时,您都会按照逻辑步骤进行思考。前任。先问号码,再问名字。这是思考cin 的错误方式。因此,您遇到了竞争条件,因为您假设每次请求输入时流都是清晰的。

如果您编写程序纯粹是为了输入,您会发现问题:

void main(void)

    double num;
    string mystr;

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

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

在上面,你在想,“首先得到一个数字。”所以你输入123 按回车,你的输出将是num=123,mystr=''。这是为什么?这是因为在流中有 123\n 并且 123 被解析为 num 变量,而 \n 仍在流中。默认情况下,阅读getline 函数的文档,它将在istream 中查找,直到遇到\n。在这个例子中,由于\n 在流中,它看起来像是“跳过”了它,但它工作正常。

要使上述操作生效,您必须输入123Hello World,这将正确输出num=123,mystr='Hello World'。那,或者你在cingetline 之间放置一个cin.ignore,这样它就会分成你期望的逻辑步骤。

这就是您需要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 &gt;&gt; double,则应用程序将产生看似奇怪的输出。

【讨论】:

【参考方案5】:

在 c++ 中使用 scanf(" %[^\n]",str) 比在 cin>> 语句后使用 cin.ignore() 更好。要做到这一点,首先您必须包含 标头。

【讨论】:

不正确。 scanf&lt;cstdio&gt; 标头的一部分,不是从用户那里获取输入的惯用 C++ 方式。【参考方案6】:

简答

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

什么时候?当您使用某些不会自行忽略前导空格的函数时。默认情况下,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 个字符已被提取,或者一个比较等于 分隔符。

如果文件结尾是,该函数也会停止提取字符 到达。如果过早地达到这一点(在提取 n 字符或查找分隔符),该函数设置 eofbit 标志。

所以,cin.ignore(256, '\n');,忽略前 256 个字符或所有字符,直到它遇到分隔符(这里是 \n 在你的情况下),以先到者为准(这里 \n 是第一个字符,所以它忽略直到遇到 \n )。

仅供参考,如果您不确切知道要跳过多少个字符,并且您的唯一目的是清除流以准备使用 getline 或 cin 读取字符串,则应使用 cin.ignore(numeric_limits&lt;streamsize&gt;::max(),'\n')

快速解释:它忽略等于流最大大小的字符或直到遇到'\n',无论哪种情况先发生。

【讨论】:

is cin.get() 是否也像 cin.getline() 一样在帐户中使用空格字符?【参考方案7】:

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

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

#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;

所以,你明白它会忽略那些不需要的输入并完成工作。

【讨论】:

以上是关于何时以及为啥需要在 C++ 中使用 cin.ignore()?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我们需要复制构造函数以及何时应该在 java 中使用复制构造函数

为啥以及何时需要在 MongoDB 中重建索引?

为啥以及何时需要在 React 中绑定函数和事件处理程序?

我何时以及为啥要在 Swift 中使用协议?

为啥以及何时需要提供我自己的删除器?

何时以及为啥需要supportedRuntime 元素和sku 属性?