如何testingstringstream操作符>>是否parsing了一个不好的types并跳过它

我有兴趣讨论使用stringstream分析多个types的行的方法。 我将首先看以下行:

 "2.832 1.3067 nana 1.678" 

现在让我们假设我有一个很长的行,有多个stringsdoubles 。 解决这个问题的显而易见的方法是标记string,然后检查每个string的转换。 我有兴趣跳过这第二步,直接使用stringstream只find数字。

我想通过这种方法来读取string,并检查是否已经设置了failbit ,如果我尝试将stringparsing为双failbit ,那么这将是一个好办法。

说我有以下代码:

 string a("2.832 1.3067 nana 1.678"); stringstream parser; parser.str(a); for (int i = 0; i < 4; ++i) { double b; parser >> b; if (parser.fail()) { std::cout << "Failed!" << std::endl; parser.clear(); } std::cout << b << std::endl; } 

它会打印出以下内容:

 2.832 1.3067 Failed! 0 Failed! 0 

我不惊讶它不能parsing一个string,但是内部发生了什么,它不能清除它的失败failbit并parsing下一个数字?

下面的代码可以很好地跳过坏词并收集有效的double

 istringstream iss("2.832 1.3067 nana 1.678"); double num = 0; while(iss >> num || !iss.eof()) { if(iss.fail()) { iss.clear(); string dummy; iss >> dummy; continue; } cout << num << endl; } 

这是一个完整的工作示例 。


您的示例几乎没有错,只是在检测到错误的格式后,才使用stream中的无效input字段

  if (parser.fail()) { std::cout << "Failed!" << std::endl; parser.clear(); string dummy; parser >> dummy; } 

在你的情况下,提取将尝试再次从"nana"读取最后一次迭代,因此输出中的最后两行。

还要注意关于iostream::fail()的诡计,以及如何在我的第一个示例中实际testingiostream::eof() 。 有一个众所周知的问答 ,为什么EOF的简单testing作为一个循环条件被认为是错误的。 而且,当遇到意外/无效的值时,如何中断input循环,情况会好一些。 但是,如何跳过/忽略无效的input字段没有解释那里(并没有要求)。

与πάνταῥεῖ的答案几乎没有什么区别 – 使它也处理负数表示等,以及作为 – 恕我直言 – 一个简单的阅读。

 #include <iostream> #include <sstream> #include <string> int main() { std::istringstream iss("2.832 1.3067 nana1.678 x-1E2 xxx.05 meh.ugh"); double num = 0; for (; iss; ) if (iss >> num) std::cout << num << '\n'; else if (!iss.eof()) { iss.clear(); iss.ignore(1); } } 

输出:

 2.832 1.3067 1.678 -100 0.05 

(看到它在这里运行)

我已经build立了一个更好的调整版本,这是可以跳过无效input字符明智(不需要用空白字符分开double数字):

 #include <iostream> #include <sstream> #include <string> using namespace std; int main() { istringstream iss("2.832 1.3067 nana1.678 xxx.05 meh.ugh"); double num = 0; while(iss >> num || !iss.eof()) { if(iss.fail()) { iss.clear(); while(iss) { char dummy = iss.peek(); if(std::isdigit(dummy) || dummy == '.') { // Stop consuming invalid double characters break; } else { iss >> dummy; // Consume invalid double characters } } continue; } cout << num << endl; } return 0; } 

产量

  2.832 1.3067 1.678 0.05 

现场演示

如果你喜欢简洁 – 这是另一个选项(ab?)使用&&只有当一个数字被parsing成功,并且当一个数字没有被parsing时,它使用逗号操作符才能clear()stream错误状态在阅读一个字符之前有条件的内容被忽略…

 #include <iostream> #include <sstream> #include <string> int main() { std::istringstream iss("2.832 1.3067 nana1.678 x-1E2 xxx.05 meh.ugh"); double num = 0; char ignored; while (iss >> num && std::cout << num << '\n' || (iss.clear(), iss) >> ignored) ; } 

http://ideone.com/WvtvfU

你可以使用std::istringstream::eof()validation这样的input:

 #include <string> #include <sstream> #include <iostream> // remove white-space from each end of a std::string inline std::string& trim(std::string& s, const char* t = " \t") { s.erase(s.find_last_not_of(t) + 1); s.erase(0, s.find_first_not_of(t)); return s; } // serial input std::istringstream in1(R"~( 2.34 3 3.f 3.d .75 0 wibble )~"); // line input std::istringstream in2(R"~( 2.34 3 3.f 3.d .75 0 wibble )~"); int main() { std::string input; // NOTE: This technique will not work if input is empty // or contains only white-space characters. Therefore // it is safe to use after a conditional extraction // operation >> but it is not reliable after std::getline() // without further checks. while(in1 >> input) { // input will not be empty and will not contain white-space. double d; if((std::istringstream(input) >> d >> std::ws).eof()) { // d is a valid double std::cout << "d1: " << d << '\n'; } } std::cout << '\n'; while(std::getline(in2, input)) { // eliminate blank lines and lines // containing only white-space (trim()) if(trim(input).empty()) continue; // NOW this is safe to use double d; if((std::istringstream(input) >> d >> std::ws).eof()) { // d is a valid double std::cout << "d2: " << d << '\n'; } } } 

这是有效的,因为eof()检查可以确保inputdouble,而不是像12d4那样的垃圾。