最快的方法将一串数字解析为整数向量

时间:2014-09-11 22:12:26

标签: c++ boost vector stl

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

>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都可以。

4 个答案:

答案 0 :(得分:1)

你必须使用助推吗? 我已经使用了这个功能一段时间了。我相信我是从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 此外,您还需要签入以跳过标题。应该是微不足道的。

请注意,我没有编译上面的内容。 ;)

答案 1 :(得分:1)

关于这个具体问题,如果你想要最快,我建议一次手动解析1个字符。提升精神可能会紧随其后,为你节省很多丑陋的代码。

一次手动解析一个char是高速的关键,因为即使是优化的转换器(如atoi和strtol)也必须处理许多不同的数字表示,而您的示例似乎暗示您只对纯无符号整数感兴趣。格式化的IO(scanf,operator&lt;&lt;等)非常慢。将线条读入中间字符串可能会产生可见的成本。

您的问题很简单,只需手动解析,假设标题行不包含任何&#39; \ t&#39; (并假设没有任何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;
    }
}

答案 2 :(得分:1)

与往常一样,如果有这样令人愉快的问题,那么除了展示“一种方式”做“一件事”之外别无其他。在这种情况下,我使用了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 ]

答案 3 :(得分:0)

您可以使用类似以下内容而不是我使用的字符串数组,您将从文件中获取字符串

#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 

至于标题,那么你可以检查tmp是否是一个标题,如果是,你将跳过这个记录。

这是简化版

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

输出与上面相同。