程序跳过了 cin.getline() 函数。

5

我制作了这个程序,它获取用户的地址、姓名和工作。然后将它们组合成一个字符串并输出该字符串。(我知道有更好的方法来做到这一点)

char str[600];
char adrs[200];
char name[10];
char wrk[200];
cout<<"Enter your name and press ENTER: ";
cin.getline(name,10);
cout<<"\nEnter your adress and press ENTER:";
cin.getline(adrs,200);
cout<<"\nEnter your workplace and press ENTER:";
cin.getline(wrk,200);
strcpy(str,"My name is ");
strcat(str,name);
strcat(str,"\nand i live at ");
strcat(str,adrs);
strcat(str, "\nI also work at ");
strcat(str, wrk); strcat(str, "\n\n");
cout<<str<<endl;

当我输入一个超过10个字符的名称时,程序确实按照我的预期使用用户输入的前9个字符,但在此之后,它跳过了程序中所有下一个cin.getline(),直接输出str并结束程序。
为什么会这样?怎样解决这个问题?

你是否可能打错了字,例如你本意是 100,但却写成了 10 - DGH
4
不要使用成员函数getline,使用自由函数std::getlinestd::string - Kerrek SB
代码中有一个错别字,应该是10而不是100。 - Mohamed Ahmed Nabil
@MohamedAhmedNabil,那又怎样,没有超过200/100个符号的地址或名称吗? - Aleksei Zabrodskii
我只是用10来进行简单测试。写1000个字符来测试会很麻烦。 - Mohamed Ahmed Nabil
std::getline(std::cin, input) - Vortico
5个回答

8
每次使用cin时,它会将输入的每个字符存储在内存中,直到遇到换行符。这块内存称为输入缓冲区。你第一次调用cin.getline()请求一个包括终止空字符在内的10个字符的字符串。然而,cin愉快地读取用户键入的所有字符,直到他按下回车键。如果用户键入超过9个字符,则cin将剩余的字符存储在输入缓冲区中,并在以后的输入操作中使用它们。例如,如果用户键入15个字符,则你对cin.getline()的调用将把前九个字符存储在c字符串数组中。再次调用cin.getline()将继续读取已经输入的剩余部分。
为了解决这个问题,你应该使用cin.ignore()跳过这个换行符。我强烈建议你熟悉C++库的在线参考资料。我的两个最爱是http://www.cplusplus.comhttp://www.cppreference.com

编辑:为了使我的答案更完整,我还需要补充一点,即如果输入缓冲区中的字符数超过了请求的字符数,cin.getline()将设置故障位。在使用cin进行任何其他输入操作之前,必须调用cin.clear()来清除故障位。


2
-1,std::cin.flush不存在。至少在2003年的C++标准中,§27.6.1.1没有将flush列为basic_istream的成员。 - Robᵩ
1
@Robᵩ 不好意思,应该是 cin.ignore()。我已经修改了我的答案,并附上了 www.cppreference.com 的链接。 - Code-Apprentice
@MohamedAhmedNabil 我已经添加了一些细节来帮助解释输入缓冲区的工作原理。 - Code-Apprentice
1
这是错误的。来自http://www.cplusplus.com/reference/iostream/istream/getline/,“如果找到分隔符,则会被提取并丢弃,即不会被存储,下一个输入操作将在其后开始。” - Mark Ransom
1
我仍然有同样的问题。而且Cin.getline在其他Cin.getline()中不会继续读取其余的输入。它只是跳过它们,最后程序除了名称之外什么都不打印。 - Mohamed Ahmed Nabil
显示剩余10条评论

3

当读取的字符串过长时,流将会设置 ios_base::failbit,并且所有随后对该流的操作都将失败。您需要使用 ios::clear 重置失败状态:

if (cin.fail())
    cin.clear();

在错误被清除后,您可能想要使用istream::ignore来忽略该行的其余内容。 编辑:我得到了一条有用的提示才能正确使用ignore。以下是完整的解决方案。
cout<<"Enter your name and press ENTER: ";
cin.getline(name,10);
if (cin.fail())
{
    cin.clear();
    cin.ignore(numeric_limits<streamsize>::max(), '\n');
}

我建议将所有代码放入一个函数中,这样你就可以在每个getline之后调用它。

cin.ignore(std::numeric_limits<int>::max(), '\n'); 的作用类似于手工编写的循环。但是,你是正确的,真正的解决方案是清除失败位。 - Code-Apprentice
@Code-Guru,谢谢!由于某些原因我没有看到第二个参数 - 我现在会进行更改。 - Mark Ransom
两个参数都有默认值。第二个默认为EOF,但在我的看法中对于std::cin并不是很有用。 - Code-Apprentice
哦,我的第一个参数的版本可能可以编译,但从技术上讲并不正确,因为它的类型实际上是一个typedef。我没有花时间查找应该使用的确切类型。 - Code-Apprentice

2

cin.getline(name,10);从输入中最多读取九个字符,如果有换行符就会提前停止。如果这九个字符中没有换行符,则不会读取该行的其余内容,并且下一次调用getline将继续读取该行的剩余部分。

如果你想忽略输入行的其余部分,可以使用以下方法:

cin.get(name, 10);
cin.ignore(INT_MAX, '\n');

1

免费函数std::getline适用于std::string,比字符数组更容易处理。特别是,std::getline允许任意大的输入行。

试试这个:

std::string str;
std::string adrs;
std::string name;
std::string wrk;
cout<<"Enter your name and press ENTER: ";
std::getline(std::cin, name);
cout<<"\nEnter your adress and press ENTER:";
std::getline(std::cin, adrs);
...

0

你的逻辑是正确的,问题在于当你声明一个大小为10的字符数组时,你实际上只有9个位置可以放字符,最后一个位置被保留用来表示字符串的结尾。[这里][1]提供了更多关于这个问题的参考资料:

只需声明比你需要的数量多一个字符即可。还有许多优秀的技巧可以用来清空流、忽略其余的行等。

祝你好运!


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