输入流迭代器和异常

5

几天前我在尝试使用istream迭代器和异常处理,然后发现了一个有趣的问题:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

using namespace std;

int main(int argc, char* argv[])
{
   if (argc < 2) {
      cout << argv[0] << " <file>" << endl;
      return -1;
   }

   try {
      ifstream ifs(argv[1]);
      ifs.exceptions(ios::failbit | ios::badbit);
      istream_iterator<string> iss(ifs), iss_end;
      copy(iss, iss_end, ostream_iterator<string>(cout, "\n"));
   }
   catch (const ios_base::failure& e) {
      cerr << e.what() << endl;
      return -2;
   }

   return 0;
}

为什么在读取输入文件的最后一个单词后总是会引发failbit异常?

有人能修正一下格式吗?谢谢。 - Jürgen A. Erhard
我在C++编程时从未使用过迭代器读取文件,但在标准读取循环中,我有一个特殊的条件来判断文件结束(即在非EOF情况下进行读取)。 - Roman
2
@Roman:那你做错了。只测试EOF会导致无限循环,因为读取错误时永远不会到达文件结尾。最好的做法是始终同时测试EOF和失败。另一方面,OP的代码使用异常处理,因此是安全的。 - Konrad Rudolph
@Konrad Rudolph:我只在ACM竞赛中使用C++,因为输入数据格式是严格预定义的。 - Roman
@Roman:在大多数情况下,数据格式是严格定义的。这并不改变你做错了的事实。 - Martin York
3个回答

4

failbit是在读取操作无法提取任何字符时设置的,无论是因为它遇到了EOF还是其他原因。

stringstream ss ("foo");
string s;
int i;

ss >> i; // sets failbit because there is no number in the stream
ss.clear();
ss >> s; // sets eofbit because EOF is hit
ss.clear();
ss >> s; // sets eofbit and failbit because EOF is hit and nothing is extracted.

1

好问题。如果能够捕获该调用中的其他故障,但在遇到eof时仍然正常运行,那将是很好的。

话虽如此,我以前没有使用过流异常。我认为你可以进行复制并检查流的状态以便检测其他错误,例如:

ifstream ifs(argv[1]);
if (!ifs) {
    cerr << "Couldn't open " << argv[1] << '\n';
    return -1;
}
//ifs.exceptions(ios::failbit | ios::badbit);
istream_iterator<std::string> iss(ifs), iss_end;
copy(iss, iss_end, ostream_iterator<std::string>(cout, "\n"));
if (!ifs.eof()) {
    cerr << "Failed to read the entire file.\n";
    return -2;
}

@Roman:在这个程序中,我无法自己测试文件结束的条件,因为我无法控制读取循环。它是算法“copy”必须进行测试。无论如何,istream_iterator的默认构造函数创建了一个“过去末尾”的迭代器(使用STL术语),在这种情况下是EOF,以完成循环(“copy”的第二个参数)。 - null_pointer
@AProgrammer:但这就是程序的诀窍(也是我提问的原因)。我没有在ifs.exceptions(...)中指定ios::eofbit,所以“copy”应该正常到达文件结尾并且不会引发任何异常。我的意思是,如果我像这样做了:ifs.exceptions(ios::failbit | ios::badbit | ios::eofbit),你所说的就是正确的。 - null_pointer
你似乎把评论放错了位置 :) 但我认为 istream_iterator 的结束是在它失败时而不是在 eof 处。此外,这些条件似乎无法可靠地分开测试。例如,在 coppro 的回复中在 "foo " 中添加一个空格,你会发现 eofbit 和 failbit 同时设置。所以要么你不使用异常,要么你接受 EOF 是异常的... - UncleBens
对不起,UncleBens!我以为这个文本框是页面的“全局”直到我意识到每个回复都有自己的文本框。我宁愿听从您的建议,不使用异常。谢谢。还要感谢大家。 - null_pointer

0

通过读取直到失败(触发异常),然后检查失败的原因来检测EOF条件。

扩展一下:当使用 >> 读取一个值后,流操作符 void* 返回 NULL 时,istream_iterator 就会变得无效。但是为了达到这个目的,>> 操作符必须设置失败位,从而引发异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接