使用C++进行简单的字符串解析

And*_*nck 33 c++

我已经使用C++很长一段时间了但是scanf当我必须解析简单的文本文件时,我倾向于依赖它.例如,给定这样的配置(也假设字段的顺序可能不同):

foo: [3 4 5]
baz: 3.0
Run Code Online (Sandbox Code Playgroud)

我会写一些类似的东西:

char line[SOME_SIZE];
while (fgets(line, SOME_SIZE, file)) {
    int x, y, z;
    if (3 == sscanf(line, "foo: [%d %d %d]", &x, &y, &z)) {
        continue;
    }
    float w;
    if (1 == sscanf(line, "baz: %f", &w)) {
        continue;
    }
}
Run Code Online (Sandbox Code Playgroud)

在C++中实现这一目的最简洁的方法是什么?每当我尝试时,我最终会得到很多脚手架代码.

Nik*_*kko 29

这是一次只使用标准C++的尝试.

大多数时候我使用std :: istringstream和std :: getline(它可以分离单词)的组合来获得我想要的东西.如果我可以使我的配置文件看起来像:

FOO = 1,2,3,4

这让事情变得简单.

文本文件是这样的:

foo=1,2,3,4
bar=0
Run Code Online (Sandbox Code Playgroud)

你像这样解析它:

int main()
{
    std::ifstream file( "sample.txt" );

    std::string line;
    while( std::getline( file, line ) )   
    {
        std::istringstream iss( line );

        std::string result;
        if( std::getline( iss, result , '=') )
        {
            if( result == "foo" )
            {
                std::string token;
                while( std::getline( iss, token, ',' ) )
                {
                    std::cout << token << std::endl;
                }
            }
            if( result == "bar" )
            {
               //...
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 很难说. (5认同)
  • 我不知道为什么我仍然为此投票.你为什么不发表评论让我明白?有人反对std :: getline?我只是尝试使用标准C++来提供解决方案.是因为sbi评论?但我改变了代码! (2认同)
  • 1用于发布std c ++响应 (2认同)

小智 19

C++字符串工具箱库(StrTk)具有以下问题的解决方案:

#include <string>
#include <deque>
#include "strtk.hpp"

int main()
{
   std::string file_name = "simple.txt";
   strtk::for_each_line(file_name,
                       [](const std::string& line)
                       {
                          std::deque<std::string> token_list;
                          strtk::parse(line,"[]: ",token_list);
                          if (token_list.empty()) return;

                          const std::string& key = token_list[0];

                          if (key == "foo")
                          {
                            //do 'foo' related thing with token_list[1] 
                            //and token_list[2]
                            return;
                          }

                          if (key == "bar")
                          {
                            //do 'bar' related thing with token_list[1]
                            return;
                          }

                       });

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

更多例子可以在这里找到


Tho*_*tit 5

Boost.Spirit不保留解析复杂的结构.它在微分析方面也相当不错,几乎与C + scanf片段的紧凑性相匹配:

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

using namespace boost::spirit::qi;


int main()
{
   std::string text = "foo: [3 4 5]\nbaz: 3.0";
   std::istringstream iss(text);

   std::string line;
   while (std::getline(iss, line))
   {
      int x, y, z;
      if(phrase_parse(line.begin(), line.end(), "foo: [">> int_ >> int_ >> int_ >> "]", space, x, y, z))
         continue;
      float w;
      if(phrase_parse(line.begin(), line.end(), "baz: ">> float_, space , w))
         continue;
   }
}
Run Code Online (Sandbox Code Playgroud)

(为什么他们没有添加"容器"版本超出我的范围,如果我们可以写下来会更方便:

if(phrase_parse(line, "foo: [">> int_ >> int_ >> int_ >> "]", space, x, y, z))
   continue;
Run Code Online (Sandbox Code Playgroud)

但确实如此:

  • 它增加了大量的编译时间开销.
  • 错误消息是残酷的.如果你使用scanf犯了一个小错误,你只需运行你的程序并立即获得段错误或荒谬的解析值.用精神犯一个小错误,你将从编译器得到无望的巨大错误信息,并需要很多练习与boost.spirit来理解它们.

所以最终,对于简单的解析,我像其他人一样使用scanf ......