getline 设置 failbit 以及 eof

getline setting failbit along with eof

提问人:gnikit 提问时间:6/3/2018 最后编辑:gnikit 更新时间:6/3/2018 访问量:1953

问:

我知道这种行为的起源,因为它在 SO 的多篇文章中得到了很好的解释,一些值得注意的例子是:

为什么循环条件中的 iostream::eof 被认为是错误的?

在不设置 failbit 的情况下使用 getline()

std::getline 命中 eof 时抛出

C++ istream EOF 不保证失败吗?

它也包含在 std::getline 标准中:

3) 如果出于任何原因没有提取任何字符(甚至没有提取丢弃的分隔符),getline 将设置 failbit 并返回。

我的问题是如何处理这种行为,您希望您的流捕获所有情况的异常,但通过到达最后一行为空的文件引起的异常除外。我是否遗漏了什么明显的内容?failbiteof

一个 MWE:

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>


void f(const std::string & file_name, char comment) {

std::ifstream file(file_name);
file.exceptions(file.failbit);
    try {
          std::string line;

          while (std::getline(file, line).good()) {
          // empty getline sets failbit throwing an exception
            if ((line[0] != comment) && (line.size() != 0)) {
                std::stringstream ss(line);
                // do stuff
            }
        }
    }

    catch (const std::ios_base::failure& e) {
        std::cerr << "Caught an ios_base::failure.\n"
        << "Explanatory string: " << e.what() << '\n'
        << "Error code: " << e.code() << '\n';

        }
}


int main() {

    f("example.txt", '#');
}

其中 example.txt 是制表符分隔的文件,其最后一行仅是字符:\n

# This is a text file meant for testing
0   9
1   8
2   7

编辑:

while(std::getline(file, line).good()){...}复制问题。

C++ 异常 GetLine EOF

评论


答:

2赞 jcai 6/3/2018 #1

编辑:我误解了OP,请参阅上面David的回答。此答案用于检查文件是否具有终止换行符。

在循环结束时,检查 .while (getline)file.eof()

假设您刚刚对文件中的最后一行执行了操作。std::getline()

  • 如果后面有分隔符,则已读取分隔符并且没有设置 .(在这种情况下,下一个将设置 。\nstd::getline()eofbitstd::getline()eofbit

  • 而如果没有,则已读取 EOF 并设置了 .\nstd::getline()eofbit

在这两种情况下,下一个都将触发并输入异常处理程序。std::getline()failbit

PS:如果为空,则该行为 UB。条件的顺序需要颠倒。if ((line[0] != comment) && (line.size() != 0)) {line

评论

1赞 David C. Rankin 6/3/2018
我不确定检查是否正确。无论情况如何(除非你添加),都将是第一个设置的,无论在循环中检查。我不确定我是否遵循您打算如何实施检查?就其位置而言,错误报告和代码正在file.eof()peek()getline()eofbitfile.eof()basic_ios::clear: iostream erroiostream:1
0赞 jcai 6/3/2018
@DavidC.Rankin 如果文件以换行符结尾,则 a 将设置 ,然后下一个将设置 .如果没有,则单个将同时发送。检查将捕获此内容。我认为 OP 只是想区分这两种情况。getlineeofbitgetlinefailbitgetlineeofbitfailbit
0赞 David C. Rankin 6/3/2018
是的,是的,我完全同意你对发生的事情的分析,我唯一质疑的是内部循环将如何提供帮助。f.eof()
0赞 jcai 6/3/2018
@DavidC.Rankin 啊,我明白你在说什么了,看来我误会了OP。您的解决方案是正确的。
3赞 David C. Rankin 6/3/2018 #2

避免设置的另一种方法是简单地重构测试以检测空行的读取。由于这是本例中的最后一行,因此您可以简单地避免抛出错误,例如:failbitifreturn

    std::ifstream file (file_name);
    file.exceptions (file.failbit);
    try {
        std::string line;

        while (std::getline(file, line)) {
            // detect empty line and return
            if (line.size() == 0)
                return;
            if (line[0] != comment) {
                std::stringstream ss(line);
                // do stuff
            }
        }
    }
    ...

您的另一种选择是检查是否设置在 .如果已设置 -- 读取成功完成。例如eofbitcatcheofbit

    catch (const std::ios_base::failure& e) {
        if (!file.eof())
            std::cerr << "Caught an ios_base::failure.\n"
            << "Explanatory string: " << e.what() << '\n'
            << "Error code: " /* << e.code() */ << '\n';
    }

评论

0赞 gnikit 6/3/2018
根据我的理解/测试,之后的语句将不起作用,因为在读取空行的情况下,它将设置 ,退出循环并导致异常。我的目的只是为了避免在文件中的数据之间存在偶然的空行中存储空行,它不是为了减轻异常抛出。line.size()==0std::getline(file,line)getlinefailbitwhileif (line.size() != 0)
0赞 David C. Rankin 6/3/2018
当读取仅包含 的行时,提取了 ,但未存储在 中,没有 或 设置。当您检测到 时,您知道唯一会被读取的是 。它有效,我使用您的数据文件对其进行了测试,在验证之前,您也避免调用。getline'\n''\n'linegcount = 1eofbitfailbitline.size() = 0'\n':)line[0] != commentline.size() != 0
2赞 David C. Rankin 6/3/2018
@nikjohn - 另一个简单的解决方法是将 作为第一行包含在 中。if (!file.eof())catch
0赞 gnikit 6/3/2018
这是我最初的方法,但该声明在 MWE 中完全没有效果,至少可以说这很奇怪
1赞 David C. Rankin 6/3/2018
我怀疑这是 UB @Arcinide 的副作用,这是由于访问空时引起的。扭转你的测试也可以避免这种情况。line[0] != commentlineline.size() != 0 && line[0] != comment