C++ 为什么std::getline()在格式化提取后跳过输入?

C++ 为什么std::getline()在格式化提取后跳过输入?,c++,input,iostream,istream,c++-faq,C++,Input,Iostream,Istream,C++ Faq,我有以下代码提示用户输入他们的猫的年龄和名字: #include <iostream> #include <string> int main() { int age; std::string name; std::cin >> age; std::getline(std::cin, name); if (std::cin) { std::cout << "My

我有以下代码提示用户输入他们的猫的年龄和名字:

#include <iostream>
#include <string>

int main()
{
    int age;
    std::string name;

    std::cin >> age;
    std::getline(std::cin, name);
    
    if (std::cin)
    {
        std::cout << "My cat is " << age << " years old and their name is " << name << std::endl;
    }
}
为什么输出中省略了名称?我已经给出了正确的输入,但是代码忽略了它。为什么会发生这种情况?

为什么会发生这种情况? 这与您自己提供的输入无关,而是与默认行为
std::getline()
has有关。当您为age(
std::cin>>age
)提供输入时,您不仅提交了以下字符,而且在键入Enter时,还向流追加了一个隐式换行符:

从终端提交时,选择“输入”或“返回”时,始终会在输入后追加换行符。它还用于文件中,用于移动到下一行。在提取到
age
之后,换行符保留在缓冲区中,直到下一个I/O操作将其丢弃或读取。当控制流达到
std::getline()
时,它将看到
“\nMr.wiskers”
,并且将丢弃开头的换行符,但输入操作将立即停止。发生这种情况的原因是因为
std::getline()
的任务是尝试读取字符,并在找到新行时停止。因此,其余的输入将留在缓冲区中未读

解决方案
cin.ignore()
要解决此问题,一个选项是在执行
std::getline()
之前跳过换行符。您可以通过在第一次输入操作后调用
std::cin.ignore()
来完成此操作。它将丢弃下一个字符(换行符),使其不再碍事

std::cin >> age;
std::cin.ignore();
std::getline(std::cin, name);
匹配操作 当您遇到这样的问题时,通常是因为您将格式化输入操作与未格式化输入操作相结合。格式化输入操作是指接受输入并将其格式化为特定类型。这就是
operator>>()
的作用。无格式输入操作是除此之外的任何操作,如
std::getline()
std::cin.read()
std::cin.get()
,等等。这些函数不关心输入的格式,只处理原始文本

如果您坚持使用单一格式,则可以避免此恼人的问题:

// Unformatted I/O
std::string age, name;
std::getline(std::cin, age);
std::getline(std::cin, name);


如果您选择使用未格式化操作将所有内容作为字符串读取,则可以在以后将其转换为适当的类型。

如果您以以下方式更改初始代码,则所有内容都将正常:

if ((cin >> name).get() && std::getline(cin, state))

发生这种情况的原因是,当终端通知流开始新行时,会将一个隐式换行符(也称为换行符)追加到终端的所有用户输入中。您可以在检查多行用户输入时使用来安全地说明这一点。
std::getline
的默认行为将从输入流对象(在本例中为
std::cin
)中读取所有内容,包括换行符
\n

#include <iostream>
#include <string>

int main()
{
    std::string name;
    std::string state;

    if (std::getline(std::cin, name) && std::getline(std::cin, state))
    {
        std::cout << "Your name is " << name << " and you live in " << state;
    }
    return 0;
}

因为上面的每个人都回答了输入
10\n\r\n
的问题,所以我想回答一个不同的方法。上述所有解决方案都发布了缓冲区是否类似
10\n\n
的代码。但如果我们不知道用户在输入信息时的行为会怎样呢。用户可以键入
10\n\nMr。胡须\n
10\n\n胡须先生\n
错了。在这种情况下,上述代码可能不起作用。因此,我使用下面的函数获取字符串输入来解决这个问题

string StringInput()  //returns null-terminated string
{
    string input;
    getline(cin, input);
    while(input.length()==0)//keep taking input until valid string is taken
    {
        getline(cin, input);
    }
    return input.c_str();
}
因此,答案是:

#include <iostream>
#include <string>

int main()
{
    int age;
    std::string name;

    std::cin >> age;
    name = StringInput();
    
    std::cout << "My cat is " << age << " years old and their name is " << name << std::endl;
    
}

非常感谢。这也会起作用,因为
get()
使用下一个字符。还有
(std::cin>>name).ignore()
,这是我在前面的回答中建议的。“…因为get()而工作…”是的,没错。很抱歉给出了没有细节的答案。为什么不简单地
if(getline(std::cin,name)和&getline(std::cin,state))
?为什么不简单地
if(getline(std::cin,name)和&getline(std::cin,state))
?@FredLarson说得好。虽然如果第一次提取是一个整数或任何不是字符串的东西都不起作用。当然,这里不是这样,用两种不同的方法做同样的事情没有意义。对于整数,您可以将该行转换为字符串,然后使用
std::stoi()
,但这样做的好处并不明显。但我倾向于只使用
std::getline()
进行面向行的输入,然后以任何有意义的方式解析行。“我认为它不太容易出错。”弗雷德拉森同意。如果我有时间,也许我会把它加进去。@Albin您可能想使用
std::getline()
的原因是,如果您想捕获给定分隔符之前的所有字符,并将其输入到字符串中,默认情况下,这就是换行符。如果那些
X
数量的字符串只是单个单词/标记,那么使用
>
就可以轻松完成这项工作。否则,您将使用
>
将第一个数字输入一个整数,在下一行调用
cin.ignore()
,然后在使用
getline()
的地方运行一个循环。我相信
std::cin>>name&&std::cin>>std::skipws&&std::getline(std::cin,state)
也应该可以正常工作。(除了下面的答案)。
#include <iostream>
#include <string>

int main()
{
    std::string name;
    std::string state;

    if (std::getline(std::cin, name) && std::getline(std::cin, state))
    {
        std::cout << "Your name is " << name << " and you live in " << state;
    }
    return 0;
}
Input:

"John"
"New Hampshire"

Output:

"Your name is John and you live in New Hampshire"
string StringInput()  //returns null-terminated string
{
    string input;
    getline(cin, input);
    while(input.length()==0)//keep taking input until valid string is taken
    {
        getline(cin, input);
    }
    return input.c_str();
}
#include <iostream>
#include <string>

int main()
{
    int age;
    std::string name;

    std::cin >> age;
    name = StringInput();
    
    std::cout << "My cat is " << age << " years old and their name is " << name << std::endl;
    
}

//instead of "std::cin>>age;" use "get_untill_int(&age);" in main function.
void get_Untill_Int(int* pInput)//keep taking input untill input is `int or float`
{
    cin>> *pInput;
    /*-----------check input validation----------------*/
    while (!cin) 
    {
        cin.clear();
        cin.ignore(100, '\n');
        cout<<"Invalid Input Type.\nEnter again: ";
        cin >>*pInput;
    }
    /*-----------checked input validation-------------*/
}