当多次调用C++的getline()函数时,它并不会等待从控制台输入。

42
我正在尝试从控制台获取几个用户输入的参数,包括两个字符串、两个整数和一个浮点数。我试图使用的相关代码如下:
#include <string>
#include <iostream>
using namespace std;

// ...

string inputString;
unsigned int inputUInt;
double inputDouble;

// ...

cout << "Title: "; 
getline(cin, inputString);
tempDVD.setTitle(inputString);

cout << "Category: "; 
getline(cin, inputString);
tempDVD.setCategory(inputString);

cout << "Duration (minutes): "; 
cin >> inputUInt; 
tempDVD.setDuration(inputUInt);

cout << "Year: "; 
cin >> inputUInt; 
tempDVD.setYear(inputUInt);

cout << "Price: $"; 
cin >> inputDouble; 
tempDVD.setPrice(inputDouble);

然而,当运行程序时,代码并不等待第一个inputString被输入,而是直到第二个getline()调用才停止。因此,控制台输出看起来像这样:
标题:类别:
光标出现在类别之后。如果我现在输入,程序会跳到年份输入,不允许我输入多个字符串。这里发生了什么?

无法重现,请发布真实完整的代码。我敢打赌你的问题在其他地方。此外,不要混合格式化输入和getline() - Kerrek SB
@KerrekSB:我同意混合它们是一种不好的风格的表现,但是为什么不能混合它们有客观的理由呢?我认为混合它们是完全有效的。 - sehe
@sehe: 问题在于格式化提取不会消耗换行符,因此当您在认为已处理完所有前一行后执行getline()时,很容易得到意外的结果。我并不是说这是不可能的,但通常会使逻辑非常难以阅读和维护。 - Kerrek SB
抱歉代码不完整,程序很长。我认为问题与此块之前使用 cin 获取 int 有关。 - user754852
9个回答

40
问题在于你混用了getline()函数和输入运算符>>。
请记住,输入运算符>>会忽略前导空格,因此可以正确地跨越行边界继续读取。但是一旦成功获取输入,它将停止读取,并且不会吞咽尾随的'\n'字符。因此,如果在>>后使用getline(),通常会得到错误的结果,除非您小心地删除未读取的'\n'字符。
诀窍是不要同时使用两种类型的输入。选择合适的一种并坚持使用它。
如果全部是数字(或与输入运算符 >> 兼容的对象),则只需使用输入运算符>>(注意,字符串是唯一不对称于输入/输出的基本类型(即不友好))。
如果输入包含字符串或需要使用getline()的组合内容,则仅使用getline()并从字符串中解析出数字。
std::getline(std::cin, line);
std::stringstream  linestream(line);

int  value;
linestream >> value;

// Or if you have boost:
std::getline(std::cin, line);
int  value = boost::lexical_cast<int>(line);

22
你可以使用

cin.ignore();

或者如@kernald所提到的使用

cin.clear();
cin.sync();

使用getline()之前


21

您需要清空输入缓冲区。可以使用 cin.clear(); cin.sync(); 实现。


11
对我来说,这组合方式很奇怪,只有使用 std::cin.ignore() 才能起作用。 - KeyC0de

4

按照要求使用cin.clear(),并进行适当的错误处理:

    cin.clear();
    cin.sync();

    cout << "Title: "; 
    if (!getline(cin, inputString))  exit 255;
    tempDVD.setTitle(inputString);

    cout << "Category: "; 
    if (!getline(cin, inputString))  exit 255;
    tempDVD.setCategory(inputString);

    cout << "Duration (minutes): "; 
    if (!(cin >> inputUInt)) exit 255; 
    tempDVD.setDuration(inputUInt);

    cout << "Year: "; 
    if (!(cin >> inputUInt)) exit 255; 
    tempDVD.setYear(inputUInt);

    cout << "Price: $"; 
    if (!(cin >> inputDouble)) exit 255; 
    tempDVD.setPrice(inputDouble);

2
谢谢。只有在使用cin.clear()和cin.sync()两个函数时,代码才能正常工作。 - user754852
1
将 >> 运算符与 getline() 方法混合使用的最简单方法是在调用任何 getline 前输入 cin.ignore(),以清除输入缓冲区中来自先前输入的任何剩余换行符。 - Corey Zambonie

2

还适用于ws。您可以使用getline(cin >> ws, inputString)来吃掉使用cin命令读取数据后的空格或换行符。


2
如果用户在之前的cin输入中的\n之前输入了空格,仅使用ignore本身将不足以解决问题,因此您必须使用以下代码而不是仅使用ignore()。例如,12345 \t \n将不再起作用。必须忽略所有未处理的字符。
#include <limits>
cin.ignore(numeric_limits<streamsize>::max(), '\n');

cingetline之间使用此代码。

0

cin.sync(); 用这个代替 cin.ignore( 效果最好。


0

将getline()与输入流混合使用通常是不好的做法。理论上可以手动处理使用流留下的脏缓冲区,但这是一种不必要的痛苦,应该避免。

最好使用控制台库来获取输入,这样可以为您抽象出脏工作。

看看TinyCon。您只需使用静态方法tinyConsole::getLine()替换您的getline和流调用,并且您可以随意使用它。

您可以在此处找到信息: https://sourceforge.net/projects/tinycon/


-1

函数_______将返回最后读取的字符,并将内部指针向左移动1个字符。

  1. Getline()
  2. Peek() 3.flush() 4.putback()

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