我已经了解了这种行为的起源,因为在SO上有很多篇文章对此进行了详细的解释,其中一些著名的例子包括:
为什么在循环条件中使用iostream::eof被认为是错误的?
而且它也包含在{{link5:std::getline
标准}}中:
3) 如果由于任何原因(甚至是丢弃的分隔符)都没有提取出字符,则getline将设置failbit并返回。
我的问题是如何处理这种行为,在这种情况下,您希望流捕获failbit
异常,除了由空行导致的eof
之外的所有情况。我是否遗漏了一些明显的东西?
一个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()){...}
复制了这个问题。
std::getline(file,line)
之后的line.size()==0
语句将不起作用,因为在getline
读取空行的情况下,它将设置failbit
,退出while
循环并导致异常。我使用if (line.size() != 0)
的目的仅仅是为了避免在文件数据之间存在空行的情况下存储空行,而不是为了减轻异常抛出。 - gnikitgetline
读取只包含'\n'
的行时,会提取'\n'
,但不会存储在line
中,gcount = 1
,没有设置eofbit
或failbit
。当检测到line.size() = 0
时,您知道唯一读取的是'\n'
。它有效,我使用了您的数据文件进行测试:)
在验证line.size() != 0
之前,还避免调用line[0] != comment
。 - David C. Rankincatch
的第一行包含if (!file.eof())
。 - David C. Rankinline
为空时访问line[0] != comment
所引起的@Arcinide注意到的UB的副作用。将您的测试反转为line.size() != 0 && line[0] != comment
也可以避免这种情况。 - David C. Rankin