检查流是否以换行符结尾

时间:2016-09-26 13:22:51

标签: c++ stream ifstream eof

我想检查一个流(实际上是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)。我一般可以这样做吗?

2 个答案:

答案 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]:

  

效果:如果sentryis.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