使用 std::getline 检测输入结束
Detecting end of input using std::getline
我有一个代码片段如下:
std::string input;
while(std::getline(std::cin, input))
{
//some read only processing with input
}
当我运行程序代码时,我通过文件重定向 stdin 输入.txt(它是使用 gedit 创建的),它包含:
ABCD
DEFG
HIJK
以上每一行都以 in.txt 中的一个换行符结尾。
我面临的问题是,while 循环运行 3 次(每行)后,程序控件不向前移动并卡住。我的问题是为什么会发生这种情况,我该怎么做才能解决问题?
一些澄清:
我希望能够从命令行运行该程序,如下所示:
$ gcc program.cc -o out
$ ./out < in.txt
附加信息:
我做了一些调试,发现 while 循环实际上运行了 4 次(第四次输入为空字符串)。这导致循环编程停止,因为//某些对输入的只读处理无法完成其工作。
所以我的精致问题:
1)为什么第四个循环会运行?
在 while 循环的条件中使用 std::getline() 的基本原理 必须是,当 getline() 无法读取更多输入时,它会返回 零,因此 while 循环中断。
与此相反,虽然循环 而是继续使用空字符串!那么为什么在 而循环条件到底是什么?这不是很糟糕的设计吗?
2) 如何确保 while 不会在不使用 break 语句的情况下第 4 次运行?
现在,我已经使用了break 语句和字符串流,如下所示:
std::string input; char temp; while(std::getline(std::cin, input)) { std::istringstream iss(input); if (!(iss >>temp)) { break; } //some read only processing with input }
但显然必须有一种更优雅的方式。
与 DeadMG 的回答相反,我相信问题在于输入文件的内容,而不是您对换行符行为的期望。
更新:现在我有机会和gedit
一起玩,我想我明白了导致问题的原因。 gedit
显然旨在使创建最后一行没有换行符的文件变得困难(这是明智的行为)。如果您打开gedit
并键入三行输入,在每行末尾键入 Enter,然后保存文件,它实际上将创建一个 4 行文件,第 4 行为空。然后,使用您的示例,文件的完整内容将"ABCDnEFGHnIJKLnn"
。为避免创建额外的空行,请不要在最后一行的末尾键入 Enter; gedit
将为您提供所需的换行符。
(作为特殊情况,如果您根本不输入任何内容,gedit
将创建一个空文件。
请注意这个重要的区别:在 gedit
中,键入 Enter 会创建一个新行。在存储在磁盘上的文本文件中,换行符(LF、'n'
)表示当前行的末尾。
文本文件表示形式因系统而异。行尾标记最常见的表示形式是单个 ASCII LF(换行符)字符(Unix、Linux 和类似系统),以及两个字符的序列,CR 和 LF(MS Windows)。我将在这里假设类似Unix的表示。(更新:在评论中,你说你使用的是 Ubuntu 12.04 和 gcc 4.6.3,所以文本文件绝对应该是 Unix 风格的格式。
我刚刚根据您问题中的代码编写了以下程序:
#include <iostream>
#include <string>
int main() {
std::string input;
int line_number = 0;
while(std::getline(std::cin, input))
{
line_number ++;
std::cout << "line " << line_number
<< ", input = "" << input << ""n";
}
}
我创建了一个 3 行文本文件in.txt
:
ABCD
EFGH
IJHL
在文件中in.txt
每行都以单个换行符结尾。
这是我得到的输出:
$ cat in.txt
ABCD
EFGH
IJHL
$ g++ c.cpp -o c
$ ./c < in.txt
line 1, input = "ABCD"
line 2, input = "EFGH"
line 3, input = "IJHL"
$
文件末尾的最后一个换行符不会开始换行符,它只是标记当前行的末尾。(不以换行符结尾的文本文件甚至可能无效,具体取决于系统。
如果我在in.txt
末尾添加第二个换行符,我可以得到您描述的行为:
$ echo '' >> in.txt
$ cat in.txt
ABCD
EFGH
IJHL
$ ./c < in.txt
line 1, input = "ABCD"
line 2, input = "EFGH"
line 3, input = "IJHL"
line 4, input = ""
$
程序在输入文件的末尾看到一个空行,因为输入文件的末尾有一个空行。
如果您检查 in.txt
的内容,您会发现最后有两个换行符 (LF),一个用于标记第三行的末尾,另一个用于标记(空)第四行的末尾。(或者,如果它是一个Windows格式的文本文件,你会在文件的最后找到一个CR-LF-CR-LF序列。
那么你应该确保它不会在其输入上收到任何空行,或者更好的是,修改它,使其正确处理空行。它应该如何处理空行?这取决于程序需要做什么,这可能完全取决于你。您可以静默地跳过空行:
if (input != "") {
// process line
}
或者,您可以将空行视为错误:
if (input == "") {
// error handling code
}
或者,您可以将空行视为有效数据。
在任何情况下,您都应该确切地决定如何处理空行。
为什么第 4 个循环会运行?
因为文本输入包含四行。
新行字符的意思就是 - "开始新行"。这并不意味着"前面的行是完整的",在这个测试中,揭示了这两种语义之间的差异。所以我们有
1. ABCD
2. DEFG
3. HIJK
4.
第三行末尾的换行符开始一个新行 - 就像它应该做的那样,就像它的名字所说的那样。该行为空的事实就是您返回空字符串的原因。如果要避免它,请修剪第三行末尾的换行符,或者简单地修剪特殊情况if (input == "") break;
。
问题与您的代码无关,而在于您对换行符行为的错误期望。
结局:
编辑:请阅读接受的答案,以获取问题的正确解释和解决方案。
作为在 while 循环条件下使用 std::getline() 的人的注意,请记住检查它是否是循环中的空字符串并相应地中断,如下所示:
string input;
while(std::getline(std::cin, input))
{
if(input = "")
break;
//some read only processing with input
}
我的建议:在while循环条件中根本不要使用std::getline()。而是像这样使用 std::cin :
while(std::cin>>a>>b)
{
//loop body
}
这样就不需要对空字符串进行额外的检查,并且代码设计更好。
上面提到的后一种方法否定了对空字符串的显式检查(但是,最好对输入的格式进行尽可能多的显式检查)。
- 根据用户输入用字母填充矢量,并将"开始"和"结束"放在四肢
- 为什么我的程序在输入某个形状的面积的测量值后没有结束?
- 在Visual Studio中运行/调试C ++时,如何在结束时等待输入
- 确定用户字符输入 C++ 的结束
- 如何使用Chrono或ctime libaray输入设置的开始和结束时间
- 通过终端在文件中输入时检测EOF(文件结束)时出现问题
- 如何在输入结束时修复预期}错误
- cin函数是否添加了在输入结束时终止的null
- 错误:预期的“}”输入结束时 - 有一个
- 正则表达式在开头单独匹配单词,或者在输入结束时通过不重复字符串附加到某些内容但附加到某些内容
- 使用 std::getline 检测输入结束
- 使用 cin.peek() 和使用 cin.peek() 检测输入结束时出现问题
- C++查找数字总和,直到用户输入结束
- Cin,直到输入结束
- XTerm usnig键盘中的输入结束
- getline(),直到输入结束
- 自定义缓冲输入流.输入结束
- Windows 中的 Control+Z 表示输入 c++ 结束的信号
- Visual Studio 2013 c++控制台程序的输入结束
- 提振.Property_tree读取json预期的输入结束