簡體   English   中英

C++ - 如何使用 stream 解析文件?

[英]C++ - How to use a stream to parse a file?

我有一個文件,我需要循環分配一個 int foo,字符串類型,64/128 位長。 我將如何使用 stream 將這些行解析為以下變量 - 我想堅持使用 stream 語法( ifs >> foo >> type ),但在這種情況下,類型最終會成為 Z65E8800B5C680088B22A 之后的行52 ......那時我只會得到一個 char* 並使用 strtoull 等等,所以為什么首先使用 stream ......我希望可讀的代碼在 char 字符串 / strtok / strtoull 上沒有可怕的性能

//input file:
0ULL'04001C0180000000000000000EE317BC'
52L'04001C0180000000'
//ouput:
//0 ULL 0x04001C0180000000 0x000000000EE317BC
//52 L 0x04001C0180000000

  ifstream ifs("input.data");
  int foo;
  string type;
  unsigned long long ull[2];

Boost Spirit 實施

這是強制性的基於 Boost Spirit (Qi) 的實現。 為了更好地衡量,包括使用 Boost Spirit (Karma) 進行格式化:

#include <string>
#include <iostream>
#include <fstream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>

namespace karma=boost::spirit::karma;
namespace qi   =boost::spirit::qi;

static qi::uint_parser<unsigned long long, 16, 16, 16> hex16_p; // parse long hex
static karma::uint_generator<unsigned long long, 16>   hex16_f; // format long hex

int main(int argc, char** args)
{
    std::ifstream ifs("input.data");
    std::string line;
    while (std::getline(ifs, line))
    {
        std::string::iterator begin = line.begin(), end = line.end();

        int                             f0;
        std::string                     f1;
        std::vector<unsigned long long> f2;

        bool ok = parse(begin, end,
                qi::int_                    // an integer
                >> *qi::alpha               // alternatively: *(qi::char_ - '\'')
                >> '\'' >> +hex16_p >> '\'' // accepts 'n x 16' hex digits
            , f0, f1, f2);

        if (ok)
            std::cout << "Parsed: " << karma::format(
                 karma::int_ 
                 << ' ' << karma::string 
                 << ' ' << ("0x" << hex16_f) % ' '
             , f0, f1, f2) << std::endl;
        else
            std::cerr << "Parse failed: " << line << std::endl;
    }

    return 0;
}

測試運行:

Parsed: 0 ULL 0x4001c0180000000 0xee317bc
Parsed: 52 L 0x4001c0180000000

有關如何調整的信息,請參閱下面的調整和示例,例如十六進制 output

基准

我已經對@Cubbi 的版本和上面的版本進行了基准測試,上面的是你提供的樣本輸入的 100,000 倍。 這最初給 Cubbi 的版本帶來了一點優勢: 0.786s0.823s

現在,這當然不是公平的比較,因為我的代碼每次都在動態構建解析器。 像這樣從循環中取出:

typedef std::string::iterator It;

const static qi::rule<It> parser = qi::int_ >> *qi::alpha >> '\'' >> +hex16_p >> '\'';
bool ok = parse(begin, end, parser, f0, f1, f2);

Boost Spirit 僅用0.093s就成為明顯的贏家; 已經快了 8.5 倍,即使每次迭代仍在構建 karma 格式化程序。

output 格式在兩個版本中都被注釋掉了,Boost Spirit 的速度快了 11 倍以上

調整,樣本

請注意如何輕松調整內容:

//  >> '\'' >> +hex16_p >> '\'' // accepts 'n x 16' hex digits
    >> '\'' >> qi::repeat(1,2)[ hex16_p ] >> '\'' // accept 16 or 32 digits

或者像輸入一樣格式化十六進制 output:

// ("0x" << hex16_f) % ' '
karma::right_align(16, '0')[ karma::upper [ hex16_f ] ] % ""

更改樣本 output:

0ULL'04001C0180000000000000000EE317BC'
Parsed: 0 ULL 04001C0180000000000000000EE317BC
52L'04001C0180000000'
Parsed: 52 L 04001C0180000000

HTH

對於更復雜的解析器(例如boost.spirit )來說,這是一項相當微不足道的任務。

使用標准 C++ 流來解決此問題,您需要

  • a) 將'視為空格和
  • b)對字符串“04001C0180000000000000000EE317BC”進行額外的傳遞,該字符串在值之間沒有分隔符。

借用 Jerry Coffin 的樣面代碼

#include <iostream>
#include <fstream>
#include <locale>
#include <vector>
#include <sstream>
#include <iomanip>
struct tick_is_space : std::ctype<char> {
    tick_is_space() : std::ctype<char>(get_table()) {}
    static std::ctype_base::mask const* get_table()
    {
        static std::vector<std::ctype_base::mask>
               rc(table_size, std::ctype_base::mask());
        rc['\n'] = std::ctype_base::space;
        rc['\''] = std::ctype_base::space;
        return &rc[0];
    }
};

int main()
{
    std::ifstream ifs("input.data");
    ifs.imbue(std::locale(std::locale(), new tick_is_space()));
    int foo;
    std::string type, ullstr;
    while( ifs >> foo >> type >> ullstr)
    {
        std::vector<unsigned long long> ull;
        while(ullstr.size() >= 16) // sizeof(unsigned long long)*2
        {
            std::istringstream is(ullstr.substr(0, 16));
            unsigned long long tmp;
            is >> std::hex >> tmp;
            ull.push_back(tmp);
            ullstr.erase(0, 16);
        }
        std::cout << std::dec << foo << " " << type << " "
                  << std::hex << std::showbase;
        for(size_t p=0; p<ull.size(); ++p)
            std::cout << std::setw(16) << std::setfill('0') << ull[p] << ' ';
        std::cout << '\n';
    }
}

測試: https://ideone.com/lRBTq

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM