简体   繁体   English

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

[英]When and why do I need to use cin.ignore() in C++?

I wrote a very basic program in C++ which asked the user to input a number and then a string.我用 C++ 编写了一个非常基本的程序,它要求用户输入一个数字,然后输入一个字符串。 To my surprise, when running the program it never stopped to ask for the string.令我惊讶的是,在运行程序时,它从未停止请求字符串。 It just skipped over it.它只是跳过了它。 After doing some reading on StackOverflow, I found out that I needed to add a line that said:在对 StackOverflow 进行了一些阅读后,我发现我需要添加一行内容:

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

before the line that gets the string input.在获取字符串输入的行之前。 Adding that fixed the problem and made the program work.添加它解决了问题并使程序运行。 My question is why does C++ need this cin.ignore() line and how can I predict when I will need to use cin.ignore() ?我的问题是为什么 C++ 需要这个cin.ignore()行,我如何预测何时需要使用cin.ignore()

Here is the program I wrote:这是我写的程序:

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

}

Ignore is exactly what the name implies.忽略正是顾名思义。

It doesn't "throw away" something you don't need instead, it ignores the amount of characters you specify when you call it, up to the char you specify as a breakpoint.它不会“丢弃”您不需要的东西,而是会忽略您在调用它时指定的字符数量,直到您指定为断点的字符。

It works with both input and output buffers.它适用于输入和输出缓冲区。

Essentially, for std::cin statements you use ignore before you do a getline call, because when a user inputs something with std::cin , they hit enter and a '\\n' char gets into the cin buffer.本质上,对于std::cin语句,您在执行getline调用之前使用 ignore ,因为当用户使用std::cin输入某些内容时,他们按回车键并且'\\n'字符进入cin缓冲区。 Then if you use getline , it gets the newline char instead of the string you want.然后,如果您使用getline ,它会获取换行符而不是您想要的字符串。 So you do a std::cin.ignore(1000,'\\n') and that should clear the buffer up to the string that you want.所以你做了一个std::cin.ignore(1000,'\\n')并且应该清除缓冲区直到你想要的字符串。 (The 1000 is put there to skip over a specific amount of chars before the specified break point, in this case, the \\n newline character.) (将 1000 放在那里是为了在指定的断点之前跳过特定数量的字符,在本例中为 \\n 换行符。)

You're thinking about this the wrong way.你正在以错误的方式思考这个问题。 You're thinking in logical steps each time cin or getline is used.每次使用cingetline时,您都在按逻辑步骤进行思考。 Ex.前任。 First ask for a number, then ask for a name.先问号码,再问名字。 That is the wrong way to think about cin .这是考虑cin的错误方式。 So you run into a race condition because you assume the stream is clear each time you ask for a input.因此,您遇到了竞争条件,因为您每次请求输入时都假设流是畅通的。

If you write your program purely for input you'll find the problem:如果您编写程序纯粹是为了输入,您会发现问题:

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

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

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

In the above, you are thinking, "first get a number."在上面,你在想,“先得到一个数字”。 So you type in 123 press enter, and your output will be num=123,mystr='' .所以你输入123按回车键,你的输出将是num=123,mystr='' Why is that?这是为什么? It's because in the stream you have 123\\n and the 123 is parsed into the num variable while \\n is still in the stream.这是因为在流中您有123\\n并且123被解析为num变量,而\\n仍在流中。 Reading the doc for getline function by default it will look in the istream until a \\n is encountered.默认情况下,读取getline函数的文档它将在istream查找,直到遇到\\n In this example, since \\n is in the stream, it looks like it "skipped" it but it worked properly.在这个例子中,由于\\n在流中,看起来它“跳过”了它,但它工作正常。

For the above to work, you'll have to enter 123Hello World which will properly output num=123,mystr='Hello World' .要使上述工作正常运行,您必须输入123Hello World才能正确输出num=123,mystr='Hello World' That, or you put a cin.ignore between the cin and getline so that it'll break into logical steps that you expect.那,或者您在cingetline之间放置一个cin.ignore ,以便它分解为您期望的逻辑步骤。

This is why you need the ignore command.这就是您需要ignore命令的原因。 Because you are thinking of it in logical steps rather than in a stream form so you run into a race condition.因为您是以逻辑步骤而不是流形式考虑它,所以您遇到了竞争条件。

Take another code example that is commonly found in schools:再举一个学校常见的代码示例:

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

The above seems to be in logical steps.以上似乎是合乎逻辑的步骤。 First ask for first name, last name, then age.首先询问名字,姓氏,然后是年龄。 So if you did John enter, then Doe enter, then 19 enter, the application works each logic step.因此,如果您输入John ,然后输入Doe ,然后输入19 ,则应用程序会在每个逻辑步骤中工作。 If you think of it in "streams" you can simply enter John Doe 19 on the "First name:" question and it would work as well and appear to skip the remaining questions.如果您在“流”中考虑它,您可以简单地在“名字:”问题上输入John Doe 19 ,它也会起作用并且似乎跳过其余问题。 For the above to work in logical steps, you would need to ignore the remaining stream for each logical break in questions.为了使上述内容按逻辑步骤工作,您需要ignore问题中每个逻辑中断的剩余流。

Just remember to think of your program input as it is reading from a "stream" and not in logical steps.请记住将您的程序输入视为从“流”中读取而不是按逻辑步骤读取。 Each time you call cin it is being read from a stream.每次调用cin它都会从流中读取。 This creates a rather buggy application if the user enters the wrong input.如果用户输入错误,这将创建一个相当错误的应用程序。 For example, if you entered a character where a cin >> double is expected, the application will produce a seemingly bizarre output.例如,如果您输入了一个需要cin >> double的字符,应用程序将产生一个看似奇怪的输出。

Short answer简答

Why?为什么? Because there is still whitespace (carriage returns, tabs, spaces, newline) left in the input stream.因为输入流中仍有空格(回车、制表符、空格、换行符)。

When?什么时候? When you are using some function which does not on their own ignores the leading whitespaces.当您使用某些不独立的函数时,会忽略前导空格。 Cin by default ignores and removes the leading whitespace but getline does not ignore the leading whitespace on its own. Cin 默认会忽略并删除前导空格,但 getline 不会自行忽略前导空格。

Now a detailed answer.现在详细解答。

Everything you input in the console is read from the standard stream stdin.您在控制台中输入的所有内容都是从标准流 stdin 中读取的。 When you enter something, let's say 256 in your case and press enter, the contents of the stream become 256\\n .当您输入某些内容时,假设您的情况为 256 并按 Enter,流的内容将变为256\\n Now cin picks up 256 and removes it from the stream and \\n still remaining in the stream.现在 cin 选取 256 并将其从流中删除, \\n仍保留在流中。 Now next when you enter your name, let's say Raddicus , the new contents of the stream is \\nRaddicus .现在,当您输入您的姓名时,假设为Raddicus ,流的新内容是\\nRaddicus

Now here comes the catch.现在问题来了。 When you try to read a line using getline, if not provided any delimiter as the third argument, getline by default reads till the newline character and removes the newline character from the stream.当您尝试使用 getline 读取一行时,如果没有提供任何分隔符作为第三个参数,getline 默认读取直到换行符并从流中删除换行符。 So on calling new line, getline reads and discards \\n from the stream and resulting in an empty string read in mystr which appears like getline is skipped (but it's not) because there was already an newline in the stream, getline will not prompt for input as it has already read what it was supposed to read.因此,在调用新行时,getline 从流中读取并丢弃\\n并导致在 mystr 中读取一个空字符串,这看起来像 getline 被跳过(但事实并非如此),因为流中已经有一个换行符,getline 不会提示输入输入,因为它已经读取了它应该读取的内容。

Now, how does cin.ignore help here?现在,cin.ignore 在这里有什么帮助?

According to the ignore documentation extract from cplusplus.com -根据来自cplusplus.com的忽略文档摘录 -

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

Extracts characters from the input sequence and discards them, until either n characters have been extracted, or one compares equal to delim.从输入序列中提取字符并丢弃它们,直到提取了 n 个字符,或者一个比较等于 delim。

The function also stops extracting characters if the end-of-file is reached.如果到达文件尾,该函数也会停止提取字符。 If this is reached prematurely (before either extracting n characters or finding delim), the function sets the eofbit flag.如果过早地达到了这一点(在提取 n 个字符或找到 delim 之前),该函数将设置 eofbit 标志。

So, cin.ignore(256, '\\n');所以, cin.ignore(256, '\\n'); , ignores first 256 characters or all the character untill it encounters delimeter (here \\n in your case), whichever comes first (here \\n is the first character, so it ignores until \\n is encountered). , 忽略前 256 个字符或所有字符,直到遇到分隔符(在您的情况下为 \\n ),以先到者为准(此处 \\n 是第一个字符,因此它会忽略直到遇到 \\n )。

Just for your reference, If you don't exactly know how many characters to skip and your sole purpose is to clear the stream to prepare for reading a string using getline or cin you should use cin.ignore(numeric_limits<streamsize>::max(),'\\n') .仅供参考,如果您不确切知道要跳过多少个字符,而您的唯一目的是清除流以准备使用 getline 或 cin 读取字符串,您应该使用cin.ignore(numeric_limits<streamsize>::max(),'\\n')

Quick explanation: It ignores the characters equal to maximum size of stream or until a '\\n' is encountered, whichever case happens first.快速解释:它忽略等于流的最大大小的字符或直到遇到 '\\n' ,以先发生的情况为准。

When you want to throw away a specific number of characters from the input stream manually.当您想手动从输入流中丢弃特定数量的字符时。

A very common use case is using this to safely ignore newline characters since cin will sometimes leave newline characters that you will have to go over to get to the next line of input.一个非常常见的用例是使用它来安全地忽略换行符,因为 cin 有时会留下换行符,您必须转到下一行输入。

Long story short it gives you flexibility when handling stream input.长话短说,它在处理流输入时为您提供了灵活性。

Ignore function is used to skip(discard/throw away) characters in the input stream.忽略功能用于跳过(丢弃/丢弃)输入流中的字符。 Ignore file is associated with the file istream.忽略文件与文件 istream 相关联。 Consider the function below ex: cin.ignore(120,'/n');考虑下面的函数,例如:cin.ignore(120,'/n'); the particular function skips the next 120 input character or to skip the characters until a newline character is read.特定函数跳过下一个 120 个输入字符或跳过这些字符直到读取换行符。

As pointed right by many other users.正如许多其他用户所指出的那样。 It's because there may be whitespace or a newline character.这是因为可能有空格或换行符。

Consider the following code, it removes all the duplicate characters from a given string.考虑下面的代码,它从给定的字符串中删除所有重复的字符。

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

So, You get the point that it will ignore those unwanted inputs and will get the job done.因此,您明白它会忽略那些不需要的输入并完成工作。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM