7

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

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

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

ABCD
DEFG
HIJK

上述每一行都以 in.txt 文件中的一个换行符结尾。

我面临的问题是,while循环运行3次(每行)后,程序控制不前进并卡住。我的问题是为什么会发生这种情况,我能做些什么来解决这个问题?

一些澄清:

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

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

附加信息:

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

所以我提炼的问题:

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

在 while 循环的条件中使用 std::getline() 背后的基本原理必须是,当 getline() 无法读取更多输入时,它返回零,因此 while 循环中断。

与此相反,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
}

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

4

3 回答 3

9

DeadMG 的回答相反,我认为问题出在输入文件的内容上,而不是您对换行符行为的期望。


更新:现在我有机会玩了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
}

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

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

于 2013-11-13T16:26:34.277 回答
6

为什么第四个循环完全运行?

因为文本输入包含四行。

换行符的意思就是——“开始新的一行”。这并不意味着“前面的行是完整的”,在这个测试中,这两种语义之间的区别被揭示了出来。所以我们有

1. ABCD
2. DEFG
3. HIJK
4.

第三行末尾的换行符开始一个新行 - 就像它应该做的那样,正如它的名字所说的那样。该行为空的事实是您返回空字符串的原因。如果您想避免它,请在第三行末尾修剪换行符,或者简单地使用 special-case if (input == "") break;

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

于 2013-10-30T21:31:40.007 回答
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
}

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

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

于 2013-11-13T16:41:52.930 回答