【问题标题】:Quickest Way to parse a string of numbers into a vector of ints将一串数字解析为整数向量的最快方法
【发布时间】:2014-09-11 22:12:26
【问题描述】:

我想知道将一串数字解析为整数向量的最快方法是什么。我的情况是我将拥有数百万行数据,格式如下:

>Header-name
ID1    1    1   12
ID2    3    6   234
.
.
.
>Header-name
ID1    1    1   12
ID2    3    6   234
.
.
.

我想丢弃“Header-name”字段(或者稍后将其用于排序),然后忽略 ID 字段,然后将剩余的三个整数放入向量中。 我意识到我可以使用 boost split 然后在几个带有逻辑的 for 循环中进行词法转换以忽略某些数据,但我不确定这是否会给我最快的解决方案。我看过增强精神,但我真的不明白如何使用它。 Boost 或 STL 都可以。

【问题讨论】:

标签: c++ boost vector stl


【解决方案1】:

你必须使用boost吗? 我已经使用这个功能一段时间了。我相信我是从 Accelerated C++ 中得到它的,并且从那以后就一直在使用它。您的分隔符似乎是一个制表符或多个空格。如果您将分隔符传递给“”,它可能会起作用。我认为这将取决于实际存在的情况。

std::vector<std::string> split( const std::string& line, const std::string& del )
{
        std::vector<std::string> ret;
        size_t i = 0;

        while ( i != line.size() ) {

                while ( ( i != line.size() ) && ( line.substr(i, 1) == del ) ) {
                        ++i;
                }

                size_t j = i;

                while ( ( j != line.size() ) && ( line.substr(j, 1) != del ) ) {
                        ++j;
                }

                if ( i != j ) {
                        ret.push_back( line.substr( i, j - i ) );
                        i = j;
                }
        }

        return ret;
}

你可以用这个得到每一行:

int main() {
    std::string line;
    std::vector<std::string> lines; 
    while ( std::getline( std::cin, line ) ) {
        lines.push_back( line );
    }

    for ( auto it = lines.begin(); it != lines.end(); it++ ) {
        std::vector<string> vec = split( (*it) );
        // Do something
    }
}

您可以通过快速修改使其返回 std::vector。 使用 atoi( myString.c_str() ) 将每个字符串设为 int 此外,您还需要签入以跳过标题。应该是微不足道的。

请注意,我没有在上面编译过。 ;)

【讨论】:

    【解决方案2】:

    在这个特定问题上,如果你想要最快,我建议一次手动解析 1 个字符。 Boost Spirit 可能会紧随其后,并为您节省大量难看的代码。

    一次手动解析一个字符是高速的关键,因为即使像 atoi 和 strtol 这样优化好的转换器也必须处理许多不同的数字表示,而您的示例似乎暗示您只对普通的无符号整数感兴趣。格式化的 IO(scanf、operator

    假设标题行不包含任何“\t”(并假设没有任何 IO 或格式错误),您的问题很简单,可以手动解析:

    #include <iostream>
    #include <sstream>
    #include <vector>
    #include <string>
    
    std::vector<unsigned> parse(std::istream &is)
    {
        bool skipField = true;
        char c;
        unsigned value = 0;
        std::vector<unsigned> result;
        while (is.get(c))
        {
            if (('\t' == c) || ('\n' == c))
            {
                if (!skipField)
                {
                    result.push_back(value);
                }
                skipField = ('\n' == c);
                value = 0;
            }
            else if (!skipField)
            {
                value *= 10;
                value += (c - '0');
            }
        }
        return result;
    }
    
    int main()
    {
        const std::string data = ">Header-name\nID1\t1\t1\t12\nID2\t3\t6\t234\n";
        std::istringstream is(data);
        const std::vector<unsigned> v = parse(is);
        for (unsigned u: v)
        {
            std::cerr << u << std::endl;
        }
    }
    

    【讨论】:

      【解决方案3】:

      像往常一样,对于像这样令人愉快的未充分说明的问题,除了展示“一种方法”来做“一件事”之外,没有什么比这更多了。在这种情况下,我使用了 Boost Spirit(因为你提到了它):

      解析成扁平容器

      #include <boost/spirit/include/qi.hpp>
      #include <boost/fusion/adapted.hpp>
      #include <map>
      
      std::string const input(
          ">Header - name1\n"
          "ID1    1    1   12\n"
          "ID2    3    6   234\n"
          ">Header - name2\n"
          "ID3    3    3   14\n"
          "ID4    5    8   345\n"
      );
      
      using Header    = std::string;
      using Container = std::vector<int>;
      using Data      = std::map<Header, Container>;
      
      int main()
      {
          namespace qi = boost::spirit::qi;
      
          auto f(input.begin()), l(input.end());
      
          Data data;
          bool ok = qi::phrase_parse(f, l,
              *(
                  '>' >> qi::raw[*(qi::char_ - qi::eol)] >> qi::eol
                 >> *(!qi::char_('>') >> qi::omit[qi::lexeme[+qi::graph]] >> *qi::int_ >> qi::eol)
              ), qi::blank, data);
      
          if (ok)
          {
              std::cout << "Parse success\n";
              for (auto const& entry : data)
              {
                  std::cout << "Integers read with header '" << entry.first << "':\n";
                  for (auto i : entry.second)
                      std::cout << i << " ";
                  std::cout << "\n";
              }
          }
          else
          {
              std::cout << "Parse failed\n";
          }
      
          if (f != l)
              std::cout << "Remaining input: '" << std::string(f, l) << "'\n";
      }
      

      打印

      Parse success
      Integers read with header 'Header - name1':
      1 1 12 3 6 234
      Integers read with header 'Header - name2':
      3 3 14 5 8 345
      

      解析成嵌套容器

      当然,如果您希望每行有单独的向量(不要期望效率),那么您可以简单地替换 typedef:

      using Container = std::list<std::vector<int> >; // or any other nested container
      
      // to make printing work without further change:
      std::ostream& operator<<(std::ostream& os, std::vector<int> const& v)
      {
          os << "[";
          std::copy(v.begin(), v.end(), std::ostream_iterator<int>(os, " "));
          return os << "]";
      }
      

      打印

      Parse success
      Integers read with header 'Header - name1':
      [1 1 12 ] [3 6 234 ]
      Integers read with header 'Header - name2':
      [3 3 14 ] [5 8 345 ]
      

      【讨论】:

      • 我认为这是一个很好的答案,但是,当我编译时出现一些错误:/usr/include/boost/spirit/home/support/algorithm/any_if.hpp:204:72: [跳过 5 个实例化上下文] && /usr/include/boost/spirit/home/support/container.hpp:110:12: 错误:'int' 不是类、结构或联合类型 && /usr/include/boost /spirit/home/qi/operator/sequence_base.hpp:86:13: [跳过 4 个实例化上下文] && /usr/include/boost/spirit/home/qi/operator/kleene.hpp:68:17: 错误:否'struct boost::spirit::traits::container_value' 中名为 'type' 的类型我使用的是 boost1.46,这会导致错误吗?
      • 很可能。我正在使用 boost 1_55
      【解决方案4】:

      你可以使用类似下面的东西而不是我使用的字符串数组,你将从文件中获取字符串

      #include <iostream>
      #include <sstream>
      #include <string>
      #include <vector>
      #include <iterator>
      
      int main() 
      {
          std::string s[] = { "ID1    1    1   12", "ID2    3    6   234" };
          std::vector<int> v;
      
          for ( const std::string &t : s )
          {
              std::istringstream is( t );
              std::string tmp;
      
              is >> tmp;
      
              v.insert( v.end(), std::istream_iterator<int>( is ), 
                                 std::istream_iterator<int>() );
          }                         
      
          for ( int x : v ) std::cout << x << ' ';
          std::cout << std::endl;
      
          return 0;
      }
      

      输出是

      1 1 12 3 6 234 
      

      至于header,你可以检查tmp是否是header,如果是则跳过这条记录。

      这是一个简化版

      #include <iostream>
      #include <sstream>
      #include <string>
      #include <vector>
      #include <iterator>
      
      int main() 
      {
          std::string s[] = 
          { 
              "ID1    1    1   12", 
              ">Header-name", 
              "ID2    3    6   234" 
          };
      
          std::vector<int> v;
      
          for ( const std::string &t : s )
          {
              std::istringstream is( t );
              std::string tmp;
      
              is >> tmp;
      
              if ( tmp[0] == '>' ) continue;
      
              v.insert( v.end(), std::istream_iterator<int>( is ), 
                                 std::istream_iterator<int>() );
          }                         
      
          for ( int x : v ) std::cout << x << ' ';
          std::cout << std::endl;
      
          return 0;
      }
      

      输出将与上面相同。

      【讨论】:

        猜你喜欢
        • 2021-04-21
        • 2017-04-25
        • 2012-10-05
        • 1970-01-01
        • 2021-07-09
        • 1970-01-01
        • 2019-04-22
        • 1970-01-01
        相关资源
        最近更新 更多