我想检查一个流(实际上是ifstream)是否以换行符结尾。我想出了这个:
bool StreamEndsWithNewline(std::basic_istream<char> & the_stream)
{
if (the_stream.peek() == EOF) {
the_stream.clear(); //clear flags set by peek()
return false;
}
std::string line = "blah";
while (std::getline(the_stream, line)) {
// ...
}
return line.empty();
}
这个想法是,如果流的最后一行有一个\n
结束字符,那么while循环将进行一次额外的迭代(因为还没有达到eof),其中空字符串将被分配给行参数。
&#34;空&#34;的特殊情况流必须单独处理。
它似乎适用于Windows(vs2010)。我一般可以这样做吗?
答案 0 :(得分:1)
tldr;是的,这保证可以工作,除非流最初是空的。
需要考虑两个位:fail
位和eof
位。 std::getline
来自[string.io]:
构建
sentry
对象之后,如果是 sentry转换为true,调用str.erase()
,然后从is中提取字符并将它们附加到str
,就好像通过调用str.append(1, c)
[...]如果函数不提取任何字符,它会调用{ {1}}
is.setstate(ios::failbit)
来自[istream :: sentry]:
效果:如果
sentry
为is.good()
,请致电false
。否则,准备格式化或未格式化的输入。 [...]如果is.setstate(failbit)
或is.rdbuf()->sbumpc()
返回is.rdbuf()->sgetc()
,函数调用traits::eof()
考虑到所有这些,让我们来看看两个例子:
案例1:setstate(failbit | eofbit)
。第一次调用"hello\n"
,getline()
为真,我们通过the_stream.good()
提取字符,流仍为\n
,我们用{进入循环体{1}}设置为good()
。
第二次调用line
,流仍为"hello"
,因此getline()
对象转换为true,我们调用good()
。尝试提取后续字符失败,因为我们已完成流,因此设置了sentry
。这会导致返回str.erase()
转换为false,因此我们不会再次进入循环体。在循环结束时,failbit
为空。
案例2:getline()
,没有换行符。第一次调用line
,"goodbye"
为真,我们提取字符直到我们点击getline()
。流the_stream.good()
尚未设置,因此我们仍然输入循环体,行设置为eof()
。
对failbit
的第二次调用,"goodbye"
对象的构造失败,因为getline()
为false(sentry
同时检查is.good()
和{{ 1}})。由于此失败,我们不会进入调用is.good()
的{{1}}的第一步。由于这个失败,eofbit
被设置,所以我们再次不进入循环体。
在循环结束时,failbit
仍为getline()
。
案例3:str.erase()
。在这里,failbit
不会提取任何字符,因此设置line
并且永远不会输入循环,并且"goodbye"
始终为空。有几种方法可以将此案例与案例1区分开来:
peek()
查看第一个字符是否为""
。 getline()
初始化为某些哨兵非空值。在循环结束时,如果流以分隔符结束,则该行仅为空。 答案 1 :(得分:1)
您的代码有效。
但是,您可以尝试搜索流并仅测试最后一个字符或丢弃读取的字符:
this