在使用cin后使用getline(cin, s)的方法

49
我需要下面的程序获取用户输入的整行并将其放入字符串names中:
cout << "Enter the number: ";
int number;
cin >> number;

cout << "Enter names: ";
string names;

getline(cin, names);

getline()命令之前使用cin >> number命令(我猜这就是问题所在),它将不允许我输入名称。为什么?

我听说过cin.clear()命令,但我不知道它的作用是什么,也不知道为什么需要使用它。


23
假设您输入了:5<Enter>John<Enter>。然后,cin >> number 只读取了数字 5,而将换行符(Enter)留在流中。因此,当尝试使用 getline(cin,name) 读取名称时,它会一直读到行末。但请注意,这里有一个换行符准备好被读取(因此名称将为空(因为您没有在读取数字 5 后读取掉换行符)。如果您想在 >> 和 getline() 之间切换,则需要小心处理输入的末尾换行符。 - Martin York
@LokiAstari: 这是比下面所有回答都更好的_答案_。你能把它发表为这样的答案吗? - Lightness Races in Orbit
13个回答

26
cout << "Enter the number: ";
int number;
cin >> number;

cin.ignore(256, '\n'); // remaining input characters up to the next newline character
                       // are ignored

cout << "Enter names: ";
string names;
getline(cin, names);

20
把它做成另一种方式是放置一个

cin.ignore ( std::numeric_limits<std::streamsize>::max(), '\n' ); 

在执行cin>>number;之后,需要完全刷新输入缓冲区(拒绝所有额外的字符直到找到换行符)。你需要#include <limits> 来获取 max() 方法。


@jonsca:在大多数生产系统中,“拒绝所有额外字符”是可疑的...吃掉空格没问题,但丢弃未知数据很容易而且悄无声息地导致错误的结果。 - Tony Delroy
@cnicutar 这因实现而异。 - jonsca
@Tony 如果你有一个循环在cin语句之后捕获字符,那这种方法如何?毫无疑问,额外的字符会对此造成干扰。你是在谈论安全漏洞吗? - jonsca
1
“循环读入字符”...如果你的意思是 while (isspace(cin.peek())) cin.ignore()...我觉得这样挺好。关于上面提到的,我更担心用户误解输入格式要求,或者某些脚本生成的输入数据出现错误,但是由于被忽略了,所以错误的结果被成功处理了 - 这可能会导致他们信任错误的结果。如果输入不符合规范,最好让程序生成一个错误。 - Tony Delroy
@Tony 哦,好的,我误解了你想说的话。关于验证的观点很有道理。 - jonsca

11
cout << "Enter the number: ";
int number;
if (cin >> number)
{
    // throw away the rest of the line 
    char c;
    while (cin.get(c) && c != '\n')
        if (!std::isspace(c))
        {
            std::cerr << "ERROR unexpected character '" << c << "' found\n";
            exit(EXIT_FAILURE);
        }
    cout << "Enter names: ";
    string name;
    // keep getting lines until EOF (or "bad" e.g. error reading redirected file)...
    while (getline(cin, name))
        ...use name...
}
else
{
    std::cerr << "ERROR reading number\n";
    exit(EXIT_FAILURE);
}
在上面的代码中,这个部分...
    char c;
    while (cin.get(c) && c != '\n')
        if (!std::isspace(c))
        {
            std::cerr << "ERROR unexpected character '" << c << "' found\n";
            exit(EXIT_FAILURE);
        }

如果数字后面只包含空格,则可以使用 ignore 函数从流中忽略到下一个换行符,这比较简洁,但存在丢失非空格内容的风险,可能会导致文件数据损坏而被忽略。视文件内容是否可靠以及避免处理损坏数据的重要性而定,您可能关心或不关心这一点。

因此,当需要清除错误状态时,std :: cin.clear()(和 std :: cin.ignore())是有用的,例如,如果您想让用户有多次机会输入有效的数字。

int x;
while (std::cout << "Enter a number: " &&
       !(std::cin >> x))
{
    if (std::cin.eof())
    {
        std::cerr << "ERROR unexpected EOF\n";
        exit(EXIT_FAILURE);
    }

    std::cin.clear();  // clear bad/fail/eof flags

    // have to ignore non-numeric character that caused cin >> x to
    // fail or there's no chance of it working next time; for "cin" it's
    // common to remove the entire suspect line and re-prompt the user for
    // input.
    std::cin.ignore(std::numeric_limits<std::streamsize>::max());
}

使用skipws或类似功能,是否可以更简单?

对于您最初的需求,另一个简单但不够完整的替代方案是使用std::skipws在读取行之前跳过任意数量的空格...

if (std::cin >> number >> std::skipws)
{
    while (getline(std::cin, name))
        ...

...但是如果输入像 "1E6" 这样的输入(例如,某些科学家试图输入 1,000,000,但 C++ 仅支持浮点数的这种表示法),C++ 不会接受它,您会得到 number 设置为 1,而 E6 会被读取为 name 的第一个值。另外,如果您有一个有效的数字后面跟着一个或多个空行,则这些行将被静默地忽略。


3
这并没有回答问题或解决问题。-1 - Lightness Races in Orbit
@LightnessRacesinOrbit:它是如何“没有解决问题”的?第一次对getline的调用会消耗数字后面的换行符,并循环直到找到一个非空行...这在我看来是已经解决了。OP的问题并没有问“为什么”问题发生了,但确实评论说他不确定为什么需要clear - 某人两天前编辑了这个5年前的问题以添加它,从而大大改变了其主旨。 - Tony Delroy
现在你编辑了[别人的]答案,它变得更好了。 - Lightness Races in Orbit
@LightnessRacesinOrbit:我编辑了我的评论要旨,关于jonsca在2011年的回答 - 没有新内容。 - Tony Delroy

10

尝试:

int number;

cin >> number;

char firstCharacterOfNames;
cin >> firstCharacterOfNames;  // This will discard all leading white space.
                               // including new-line if there happen to be any.

cin.unget();                   // Put back the first character of the name.

std::string  names;
std::getline(cin, names);      // Read the names;

或者,如果您知道数字和名称始终会在不同行。

cin >> number;
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
std::getline(cin, names);

8
您可以在使用getline之前使用std::ws从输入缓冲区中提取任何空白字符。头文件为sstream。
cout << "Enter the number: ";
int number;
cin >> number;
cout << "Enter names: ";
string names;
cin>>ws;
getline(cin, names);

6
在使用getline()函数前,如果您使用了cin,请尝试使用cin.ignore()。
void inputstu(){
    cout << "Enter roll Number:";
    cin >> roll_no;
    cin.ignore(); //ignore the withspace and enter key
    cout << "Enter name:";
    getline(cin, stu_name);
}

1
我刚刚使用了

标签。

getline(cin >> ws,lard.i_npute);

使用标准

#include <iostream>

在我遇到回车问题时,ws操作程序(指C++中的流操纵符)可以帮助修改header。我可能会开始将循环函数嵌入类并使用构造函数和析构函数调用。

1
cout << "Enter the number: ";
int number;
cin >> number;

cout << "Enter names: ";
string names;
getline(cin, names);//works on the \n left behind
getline(cin, names);//continues and rewrites names

这很容易理解,cin >> number 留下了一个 \n 在流中,第一次使用时被分配给了 names。现在重新使用 getline 就写入了正确的值。


1

或者您可以刷新输入缓冲区以读取字符串

fflush(stdin)

它在头文件stdio.h中定义。

这段代码有效。

cout << "Enter the number: ";
int number;
cin >> number;

cout << "Enter names: ";
string names;
fflush(stdin);  //FLUSHING STDIN
getline(cin, names);

0
从概念上讲,我认为您希望每个答案都整齐地放在一行中。那么为什么不试试这个呢?
cout << "Enter the number: ";
string line;
getline(cin, line);
int number = std::stoi(line);

cout << "Enter names: ";
string names;
getline(cin, names);

代码可以正确消耗第一个换行符,如果该行正确则返回数字,否则会抛出异常。全部免费!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接