使用std :: getline检测输入结束

时间:2013-10-30 03:32:22

标签: c++ while-loop stdin getline gedit

我有一个包含以下代码段的代码:

std::string input;
while(std::getline(std::cin, input))
{   
    //some read only processing with input
}

当我运行程序代码时,我通过文件in.txt(使用gedit创建)重定向stdin输入,它包含:

ABCD
DEFG
HIJK

上面的每一行都以in.txt文件中的一行换行结束。

我面临的问题是,在while循环运行3次后(对于每一行),程序控制不会向前移动并且卡住。我的问题是为什么会发生这种情况,我该怎么做才能解决问题?

一些澄清:

我希望能够从命令行运行程序:

$ gcc program.cc -o out
$ ./out < in.txt

附加信息:

我做了一些调试,发现while循环实际上运行了4次(第四次输入为空字符串)。这导致循环编程停止,因为 //某些处理只读输入无法完成其工作。

所以我的问题很精确:

1)为什么第四个循环都在运行?

  

在while循环条件下有std :: getline()的基本原理   必须是这样,当getline()无法读取任何更多输入时,它返回   零,因此while循环中断。

     

与此相反,while循环   而是继续一个空字符串!为什么然后有getline   而循环条件呢?设计不是那么糟糕吗?

2)如何在不使用break语句的情况下确保while不会第四次运行?

  

现在我使用了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
}
     

但显然必须有更优雅的方式。

3 个答案:

答案 0 :(得分:9)

DeadMG's answer相反,我认为问题在于输入文件的内容,而不是您对换行符字符行为的期望。


更新:现在我有机会和gedit一起玩,我想我知道是什么导致了这个问题。 gedit显然是为了难以在最后一行创建没有换行符的文件(这是明智的行为)。如果打开gedit并键入三行输入,在每行的末尾键入 Enter ,然后保存文件,它实际上会创建一个4行文件,第4行空。使用您的示例,文件的完整内容将为"ABCD\nEFGH\nIJKL\n\n"。为避免创建额外的空行,只需在最后一行的末尾键入 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
}

或者您可以将空行视为有效数据。

在任何情况下,您都应该确切地决定如何处理空行。

答案 1 :(得分:6)

  

为什么第四个循环都在运行?

因为文本输入包含四行。

新行字符意味着 - “开始新行”。它并不意味着“前一行已完成”,并且在此测试中,揭示了这两种语义之间的差异。所以我们有

1. ABCD
2. DEFG
3. HIJK
4.

第三行末尾的换行符开始一个新行 - 就像它应该做的那样,就像它的名字一样。该行为空的事实是您返回空字符串的原因。如果你想避免它,请修剪第三行末尾的换行符,或者只是特殊情况if (input == "") break;

问题与您的代码无关,而在于您对换行符的行为的错误期望。

答案 2 :(得分:1)

终曲:

编辑:请阅读已接受的答案,以便正确解释问题和解决方案。


作为在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
}

这样就不需要额外检查空字符串,代码设计也更好。

上面提到的后一种方法否定了对空字符串的显式检查(但是,对输入的格式进行尽可能多的显式检查总是更好。)