std::getline无法在for循环内工作

12

我正在尝试将用户输入收集到一个字符串变量中,该变量在指定时间内接受空格。

由于通常的 cin >> str 不接受空格,因此我会使用来自<string>的 std::getline

这是我的代码:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
    int n;
    cin >> n;
    for(int i = 0; i < n; i++)
    {
        string local;
        getline(cin, local); // This simply does not work. Just skipped without a reason.
        //............................
    }

    //............................
    return 0;
}

有什么想法吗?


11
很少有事情会毫无原因地失败。 - GManNickG
在 for 循环之前尝试以下代码:std::cout << "n: " << n << std::endl; - Matthieu N.
是的。n已经被正确读取。我输入了2作为n的值,它打印出“n: 2”。 - Yana D. Nugraha
我想我解决了你的问题。但是关于你的调试,你确实尝试过2以外的其他值了吗?这个循环已经进入了,只是“跳过”了第一个条目。 - GManNickG
13个回答

21
你可以输出你存储在local中的内容来查看为什么它会失败(顺便提一句,这是一个不好的变量名 :P):
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
    int n;
    cin >> n;
    for(int i = 0; i < n; i++)
    {
        string local;
        getline(cin, local);
        std::cout << "> " << local << std::endl;
    }

    //............................
    return 0;
}
你会看到在输入数字后,它会在>后面立即打印一个换行符,然后继续输入其余内容。
这是因为getline返回了你输入的数字剩余的空行。(它读取了数字,但显然没有删除\n,所以你留下了一个空行。)你需要先去掉任何剩余的空格:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
    int n;
    cin >> n;
    cin >> ws; // stream out any whitespace
    for(int i = 0; i < n; i++)
    {
        string local;
        getline(cin, local);
        std::cout << "> " << local << std::endl;
    }

    //............................
    return 0;
}

这个按预期工作。

离题了,也许这只是为了手头的片段,但如果你不使用using namespace std;,代码往往会更容易阅读。它违背了名称空间的目的。我猜这只是为了发布在这里。


4
我倾向于在代码中显式地使用std命名空间前缀,但是在cpp文件(而不是h文件)中使用using声明也没有问题。 - Jon Reid
3
也许在这个简单的例子中,命名空间 std 中有很多内容。但是在函数级别上使用 using 指令是我会使用的最全局的方式。 - GManNickG

6

在输入数字后,声明一个字符变量来获取回车符。char ws;int n;cin>>n;ws=cin.get(); 这样可以解决这个问题。

使用cin>>ws而不是ws=cin.get(),将使得您字符串的第一个字符存储到变量ws中,而不只是清除'\n'


3

很简单,您只需要在循环结尾处放置一个 cin.get()。


2

为什么会出现这种情况: 这是因为您混合使用了cin和cin.getline。 当您使用cin输入值时,cin不仅捕获值,还捕获换行符。因此,当我们输入2时,cin实际上获取到的字符串是“2\n”。它然后将2提取到变量中,留下换行符卡在输入流中。然后,当getline()读取输入时,它看到“\n”已经在流中,认为我们必须输入了一个空字符串!显然这不是预期的结果。

旧解决方案: 一个好的经验法则是,在使用cin读取值后,从流中删除换行符。可以使用以下方法来实现:

std::cin.ignore(32767, '\n'); // ignore up to 32767 characters until a \n is removed

更好的解决方案:每当您使用 std::getline() 读取字符串时,请使用以下代码。

std::getline(std::cin >> std::ws, input); // ignore any leading whitespace characters

std::ws是一个输入操作符,它告诉std::getline()忽略任何前导空格字符。

来源:learncpp网站,转到章节(使用std::getline()输入文本)

希望这对您有所帮助。


2

我猜测你没有正确读取变量n,导致它被转换成了0。因为0不小于0,所以循环不会被执行。

我建议添加一些调试信息:

int n;
cin >> n;
std::cerr << "n was read as: " << n << "\n"; // <- added instrumentation
for // ...

不,循环确实执行了。我正在使用VC++ 2008,因此我可以通过逐行跟踪代码来进行调试。 - Yana D. Nugraha

2

您是否按下了回车键?如果没有,get line函数将返回空值,因为它正在等待行结束符...


1
你是在哪个编译器上尝试的?我在VC2008上尝试过,运行良好。如果我在g++(GCC)3.4.2上编译相同的代码,它就不能正常工作。以下是两个编译器都能正常工作的版本。我没有最新的g++编译器。
int n;
cin >> n;
string local;
getline(cin, local); // don't need this on VC2008. But need it on g++ 3.4.2. 
for (int i = 0; i < n; i++)
{
    getline(cin, local);
    cout << local;
}

1
  • n是否从输入正确初始化?
  • 您似乎没有使用getline做任何事情。这是您想要的吗?
  • getline返回一个istream引用。您将其丢弃是否有影响?

是的。我尝试打印它,而且 n 被正确初始化了。--- 我在 getline 后截断了代码。 - Yana D. Nugraha

1
重要的问题是,“你用给你这个想法的字符串做了什么,让你觉得输入被跳过了?”或者更准确地说,“你为什么认为输入被跳过了?”
如果你在调试器中单步执行,你是否使用了优化编译(允许重新排序指令)?我不认为这是你的问题,但这是一个可能性。
我认为更有可能的是字符串被填充了,但处理不正确。例如,如果你想将输入传递给旧的C函数(例如atoi()),你需要提取C风格的字符串(local.c_str())。

0

在使用getline(cin, local) 之前,只需添加 if(i == 0) { cin.ignore(); }。 这将从字符串中删除最后一个字符(\n),它是导致此问题的原因,并且仅需要在第一次循环时执行。否则,它将在每次迭代中从字符串中删除最后一个字符。例如,

i = 0 -> string
i = 1 -> strin
i = 2 -> stri

等等。


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