用于使用sscanf解析输入的C ++替代方法

时间:2014-04-25 22:33:48

标签: c++ parsing scanf

假设我的程序需要[ 0.562 , 1.4e-2 ]形式的参数(即浮点数对),我应该如何在没有正则表达式的情况下用C ++解析这个输入?我知道在用户输入时需要考虑很多极端情况,但我们假设给定的输入与上述格式非常匹配(除了更多的空格)。

在C中,我可以像sscanf(string, "[%g , %g]", &f1, &f2);那样提取两个浮点值,这非常紧凑。

在C ++中,这是我到目前为止所提出的:

std::string s = "[ 0.562 , 1.4e-2 ]"; // example input

float f1 = 0.0f, f2 = 0.0f;

size_t leftBound = s.find('[', 0) + 1;
size_t count = s.find(']', leftBound) - leftBound;

std::istringstream ss(s.substr(leftBound, count));
string garbage;

ss >> f1 >> garbage >> f2;

if(!ss)
  std::cout << "Error while parsing" << std::endl;

我该如何改进此代码?特别是,我关注garbage字符串,但我不知道如何跳过这两个值之间的,

4 个答案:

答案 0 :(得分:6)

显而易见的方法是创建一个简单的操纵器并使用它。例如,一个操纵器使用静态提供的char来确定下一个非空白字符是否是该字符,如果是,则提取它可能如下所示:

#include <iostream>
#include <sstream>

template <char C>
std::istream& expect(std::istream& in)
{
    if ((in >> std::ws).peek() == C) {
        in.ignore();
    }
    else {
        in.setstate(std::ios_base::failbit);
    }
    return in;
}

然后,您可以使用这样的构建操纵器来提取字符:

int main(int ac, char *av[])
{
    std::string s(ac == 1? "[ 0.562 , 1.4e-2 ]": av[1]);
    float f1 = 0.0f, f2 = 0.0f;

    std::istringstream in(s);
    if (in >> expect<'['> >> f1 >> expect<','> >> f2 >> expect<']'>) {
        std::cout << "read f1=" << f1 << " f2=" << f2 << '\n';
    }
    else {
        std::cout << "ERROR: failed to read '" << s << "'\n";
    }
}

答案 1 :(得分:4)

我可以负担使用助力,你可以使用精神。

  • 来自string Live On Coliru (在c ++ 03中):

  • 更新如果您实际上尝试从流中读取(这实际上有点简单,并将真正与您的其他流读取完美集成)活动): Live On Coliru (c ++ 03)

尽管这看起来更加冗长,但Spirit比sscanf更强大,更安全,更安全。它在流上运行。

另请注意,inf-infnan将按预期处理。

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_match.hpp>
#include <sstream>

namespace qi = boost::spirit::qi;

int main()
{
    std::istringstream ss("[ 0.562 , 1.4e-2 ]"); // example input
    ss.unsetf(std::ios::skipws); // we might **want** to handle whitespace in our grammar, not needed now

    float f1 = 0.0f, f2 = 0.0f;

    if (ss >> qi::phrase_match('[' >> qi::double_ >> ',' >> qi::double_ >> ']', qi::space, f1, f2))
    {
        std::cout << "Parsed: " << f1 << " and " << f2 << "\n"; // default formatting...
    } else
    {
        std::cout << "Error while parsing" << std::endl;
    }
}

答案 2 :(得分:2)

除了正则表达式之外,您可以使用Boost中的某些内容。但是如果你不能使用Boost那么你可以定义一个std::ctype<char>方面,通过将它们分类为空格来有效地忽略所有不必要的字符。您可以将此构面安装到区域设置中,并将其填充到ss中。

答案 3 :(得分:1)

使用旧式和简单:

std::istringstream inp_str("[ 0.562 , 1.4e-2 ]");
double x;
double y;
char c;
inp_str >> c; // Eat the '['
inp_str >> x; // Input the first ordinate.
inp_str >> c >> c; // Eat the space and comma.
inp_str >> y; // Input the second ordinate.

这是有效的,因为提取运算符会跳过数字类型的前导空格。