更安全但易于使用且灵活的C ++替代sscanf()

时间:2012-03-22 15:45:57

标签: c++ string parsing scanf

当我需要扫描一堆字符串中的值时,我经常发现自己严格地回到C sscanf(),因为它简单易用。例如,我可以非常简洁地从字符串中拉出几个double值:

string str;
double val1, val2;
if (sscanf(str.c_str(), "(%lf,%lf)", &val1, &val2) == 2)
{
    // got them!
}

这显然不是很C ++。我不一定认为这是一种憎恶,但我总是在寻找一种更好的方法来完成一项共同的任务。我理解读取字符串的“C ++方式”是istringstream,但是处理上面格式字符串中的括号和逗号所需的额外输入只会使得我想要使用它太麻烦。

是否有一种很好的方法可以以类似于上面的方式将内置设施弯曲到我的意愿,或者是否有一个好的C ++库以更安全的方式完成上述操作?看起来Boost.Format确实以一种很好的方式解决了输出问题,但我没有发现任何类似于输入的简洁。

3 个答案:

答案 0 :(得分:15)

我写了一些可以读取字符串和字符文字的代码。与普通流读取一样,如果它获得无效数据,则设置流的badbit。这适用于所有类型的流,包括宽流。将此位粘贴在新标题中:

#include <iostream>
#include <string>
#include <array>
#include <cstring>

template<class e, class t, int N>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e(&sliteral)[N]) {
        std::array<e, N-1> buffer; //get buffer
        in >> buffer[0]; //skips whitespace
        if (N>2)
                in.read(&buffer[1], N-2); //read the rest
        if (strncmp(&buffer[0], sliteral, N-1)) //if it failed
                in.setstate(in.rdstate() | std::ios::failbit); //set the state
        return in;
}
template<class e, class t>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e& cliteral) {
        e buffer;  //get buffer
        in >> buffer; //read data
        if (buffer != cliteral) //if it failed
                in.setstate(in.rdstate() | std::ios::failbit); //set the state
        return in;
}
//redirect mutable char arrays to their normal function
template<class e, class t, int N>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, e(&carray)[N]) {
        return std::operator>>(in, carray);
}

它会使输入字符变得非常简单:

std::istringstream input;
double val1, val2;
if (input >>'('>>val1>>','>>val2>>')') //less chars than scanf I think
{
    // got them!
}

PROOF OF CONCEPT。现在你可以cin字符串和字符文字,如果输入不是完全匹配,它就像任何其他未能正确输入的类型一样。请注意,这仅匹配不是第一个字符的字符串文字中的空格。它只有四个功能,所有这些都是脑死亡的简单。

修改

解析流是一个坏主意。使用正则表达式。

答案 1 :(得分:6)

我用过字符串解析的最好的东西是boost.spirit。它快速,安全且非常灵活。最大的优点是您可以以接近EBNF语法的形式编写解析规则

using namespace boost::spirit;

boost::fusion::vector < double, double > value_;

std::string string_ = "10.5,10.6 ";

bool result_ = qi::parse(
    string_.begin(),
    string_.end(),
    qi::double_ >> ',' >> qi::double_, // Parsing rule
    value_); // value

答案 2 :(得分:3)

我认为使用正则表达式可以轻松完成。所以在新标准中使用boost :: regex或std :: regex。之后,只需使用lexical_cast或直接将您的标记转换为浮动。