zal*_*nix 6 c++ stdin gedit getline while-loop
我有一个包含以下代码段的代码:
std::string input;
while(std::getline(std::cin, input))
{
//some read only processing with input
}
Run Code Online (Sandbox Code Playgroud)
当我运行程序代码时,我通过文件in.txt(使用gedit创建)重定向stdin输入,它包含:
ABCD
DEFG
HIJK
Run Code Online (Sandbox Code Playgroud)
上面的每一行都以in.txt文件中的一行换行结束.
我面临的问题是,在while循环运行3次后(对于每一行),程序控制不会向前移动并且卡住.我的问题是为什么会发生这种情况,我该怎么做才能解决问题?
我希望能够从命令行运行程序:
$ gcc program.cc -o out
$ ./out < in.txt
Run Code Online (Sandbox Code Playgroud)
我做了一些调试,发现while循环实际上运行了4次(第四次输入为空字符串).这导致循环编程停止,因为//只有输入读取的某些处理无法完成其工作.
所以我提出的问题:
1)为什么第四个循环运行?
在while循环条件下使用std :: getline()的基本原理必须是,当getline()无法再读取任何输入时,它返回零,因此while循环中断.
与此相反,while循环继续使用空字符串!那么为什么get循环条件中的getline呢?设计不是那么糟糕吗?
2)如何在不使用break语句的情况下确保while不会第四次运行?
现在我使用了break语句和字符串流,如下所示:
Run Code Online (Sandbox Code Playgroud)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行为空.然后,使用您的示例,文件的完整内容将是"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";
}
}
Run Code Online (Sandbox Code Playgroud)
我创建了一个3行文本文件in.txt:
ABCD
EFGH
IJHL
Run Code Online (Sandbox Code Playgroud)
在文件中,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"
$
Run Code Online (Sandbox Code Playgroud)
文件最末端的最后一个换行符不会启动换行符,它只标记当前行的结尾.(不以换行符结尾的文本文件可能甚至无效,具体取决于系统.)
如果我在末尾添加第二个换行符,我可以得到你描述的行为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 = ""
$
Run Code Online (Sandbox Code Playgroud)
程序在输入文件的末尾看到一个空行,因为输入文件的末尾有一个空行.
如果检查内容in.txt,你会在最后找到两个换行符(LF),一个用于标记第三行的结尾,另一个用于标记(空)第四行的结尾.(或者,如果它是Windows格式的文本文件,您将在文件的最后找到CR-LF-CR-LF序列.)
如果你的代码没有正确处理空行,那么你应该确保它的输入没有接收任何空行,或者更好的是,修改它以便正确处理空行.它应该如何处理空行?这取决于程序需要做什么,这可能完全取决于你.你可以默默地跳过空行:
if (input != "") {
// process line
}
Run Code Online (Sandbox Code Playgroud)
或者您可以将空行视为错误:
if (input == "") {
// error handling code
}
Run Code Online (Sandbox Code Playgroud)
或者您可以将空行视为有效数据.
无论如何,您应该确切地决定如何处理空行.
为什么第四个循环运行?
因为文本输入包含四行.
新行字符意味着 - "开始新行".它并不意味着"前一行已完成",并且在此测试中,揭示了这两种语义之间的差异.所以我们有
1. ABCD
2. DEFG
3. HIJK
4.
Run Code Online (Sandbox Code Playgroud)
第三行末尾的换行符开始一个新行 - 就像它应该做的那样,就像它的名字所说的那样.该行为空的事实是您返回空字符串的原因.如果你想避免它,修剪第三行末尾的换行符,或者只是特殊情况if (input == "") break;.
问题与您的代码无关,而在于您对换行符的行为的错误期望.