用sscanf解析输入的C++替代方案

C++ alternative for parsing input with sscanf

本文关键字:C++ 方案 输入 sscanf      更新时间:2023-10-16

假设我的程序需要形式为[ 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字符串,但我不知道如何跳过这两个值之间的,

显而易见的方法是创建一个简单的操纵器并使用它。例如,一个操纵器使用静态提供的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";
    }
}

我可以用boost,你可以用Spirit。

参见

  • 来自Coliru上的string直播(在c++03中):

  • 更新如果你真的试图从流中阅读,这里有一种方法(它实际上有点简单,并且与其他流阅读活动很好地集成了):
    也在Coliru上直播(c++03)

尽管这看起来更为冗长,但Spirit也比sscanf功能强大且类型安全。它在流上运行。

还要注意,inf-infnan将按预期进行处理。

在Coliru上直播

#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;
    }
}

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

相关文章: