【问题标题】:C++/Boost: Writing a more powerful sscanf replacementC++/Boost:编写更强大的 sscanf 替代品
【发布时间】:2011-06-29 10:43:33
【问题描述】:

我想用 C++ 编写一个函数来替换 C 的 sscanf,它将匹配项分配给迭代器。

基本上,我想要这样的东西:

string s = "0.5 6 hello";
std::vector<boost::any> any_vector;
sscanv(s, "%f %i %s", any_vector);
cout << "float: " << any_cast<float>(any_vector[0]);
cout << "integer: " << any_cast<integer(any_vector[1]);
cout << "string: " << any_cast<string>(any_vector[2]);

确切的细节可能会有所不同,但你明白了。有什么实施想法吗?

到目前为止的选项和问题:

  • std::istringstream:没有用于匹配常量表达式的操纵器
  • Boost.Regex:不确定这是否可行,而且它似乎比必要的复杂得多
  • Boost.Spirit:不要认为这适用于动态生成的格式字符串,而且它似乎也比必要的更复杂
  • sscanf:它可以工作,但是是非标准的,等等,并且使用它会需要很多开销,因为参数的数量是在编译时确定的

【问题讨论】:

  • 你愿意使用 c++0x 概念吗?
  • @Bradley:我认为它们已从该标准的草案中删除?
  • @Bradley:当然。我正在使用 gcc 4.5.1 并使用 -std=c++0x 进行编译,只要它适用于我对它持开放态度。我不想等待一些尚不可用的东西。当然,即使它还没有,我仍然有兴趣了解它,但我现在需要一个解决方案。
  • 我猜可变参数模板可能比vector(类似于en.wikipedia.org/wiki/C%2B%2B0x#Variadic_templates)更方便,尽管解析格式字符串仍然是最大的问题。
  • @UncleBens:谢谢,我会调查这些,但你说得对,它们不能解决我的主要问题。

标签: c++ boost boost-spirit boost-regex boost-spirit-qi


【解决方案1】:

那是怎么回事?

void sscanf(std::string str,
            const std::string& format,
            std::vector<boost::any>& result)
{
  std::string::const_iterator i = format.begin();
  while (i != format.end())
  {
    if (*i == '%')
    {
      ++i; // now *i is the conversion specifier
      char specifier = *i;

      ++i; // now *i is the next seperator
      std::string extract = str.substr(0, str.find(*i));

      switch (specifier) 
      {
        // matching an integer
        case 'i':
          result.push_back(boost::lexical_cast<int>(extract));
          break;
        // matching a floating point number
        case 'a': case 'e': case 'f': case 'g':
          result.push_back(boost::lexical_cast<float>(extract));
          break;
        // matching a single character
        case 'c':
          result.push_back(boost::lexical_cast<char>(extract));
          break;
        // matching a string
        case 's':
          result.push_back(extract);
          break;
        // Invalid conversion specifier, throwing an exception
        default:
          throw std::runtime_error("invalid conversion specifier");
          break;
      }
    }
    else
    {
      // if it's not a %, eat!
      str.erase(0, str.find(*i)+1);
      ++i;
    }
  }
}

缺少一些转换说明符 - 但主要是它有效。

【讨论】:

  • 谢谢,这看起来很不错。您认为这与 scanf 的速度相比如何?
  • @deuberger lexical_cast, string::find, string::erase … 可能相当昂贵——认为它会慢得多
【解决方案2】:

如果您的格式字符串是在编译时确定的,则会写入一些variadic-template printf 替换。反转这些应该可以很好地工作。

然后您可以使用 istream 的 >> 运算符进行读取,或者使用 c-stdlib 函数。

【讨论】:

  • 这可能是最好的解决方案,但理想情况下,我更喜欢可以处理动态格式字符串(即在运行时加载)的东西,但我认为这只有在编译时指定格式时才有效时间,这与 sscanf 的限制基本相同。
  • 我明白了。嗯,这也不应该太难。如果您的解析需求是基本的,我不明白为什么您不能使用 istream 从缓冲区中读取 int/float/string,然后将它们填充到您的任意向量中(或者将其存储在输出迭代器中,如果您想要花哨)。
  • OTOH,如果您的格式字符串是动态的,那么您的向量的类型和长度也将是动态的,因此您必须对在那里处理的内容非常通用,但也许您的用例和其他代码匹配。 (我有点好奇你想做什么...... :)
  • 我说我的解析需求不仅仅是基本的,但不是高级的。我基本上想复制 sscanf 的功能,但让它更具动态性。如果没有人有更好的建议,我最终可能会使用 istream 选项。谢谢。
  • 哎呀,没有看到你最后的评论......是的,我的矢量也必须是动态的。用例需要它(至少要获得通用解决方案)。我计划使用 Boost.Any,或者根据这个概念制作我自己的版本,以更好地满足我的需求。简而言之,我想要解析大量各种格式的“文本文件”,并从中创建趋势数据。我更愿意编写和编译一次,然后使用配置文件来添加对新文件格式的支持,而不是总是必须添加更多代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-10
相关资源
最近更新 更多