5

次のような行で構成される、サイズがおそらく数 GB のテキスト ファイルを解析しています。

11 0.1
14 0.78
532 -3.5

基本的に、1 行に 1 つの int と 1 つの float があります。int は順序付けられ、負でない必要があります。データが説明どおりであることを確認したいのですが、範囲内の最小および最大の int が返されました。これは私が思いついたものです:

#include <iostream>
#include <string>

#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/std_pair.hpp>

namespace px = boost::phoenix;
namespace qi = boost::spirit::qi;

namespace my_parsers
{
using namespace qi;
using px::at_c;
using px::val;
template <typename Iterator>
struct verify_data : grammar<Iterator, locals<int>, std::pair<int, int>()>
{
    verify_data() : verify_data::base_type(section)
    {
        section
            =  line(val(0))    [ at_c<0>(_val) = _1]
            >> +line(_a)       [ _a = _1]
            >> eps             [ at_c<1>(_val) = _a]
            ;

        line
            %= (int_ >> other) [
                                   if_(_r1 >= _1)
                                   [
                                       std::cout << _r1 << " and "
                                       << _1 << val(" out of order\n")
                                   ]
                               ]
            ;

        other
            = omit[(lit(' ') | '\t') >> float_ >> eol];
    }
    rule<Iterator, locals<int>, std::pair<int, int>() > section;
    rule<Iterator, int(int)> line;
    rule<Iterator> other;
};
}

using namespace std;
int main(int argc, char** argv)
{
    string input("11 0.1\n"
                 "14 0.78\n"
                 "532 -3.6\n");

    my_parsers::verify_data<string::iterator> verifier;
    pair<int, int> p;
    std::string::iterator begin(input.begin()), end(input.end());
    cout << "parse result: " << boolalpha
         << qi::parse(begin, end, verifier, p) << endl; 
    cout << "p.first: " << p.first << "\np.second: " << p.second << endl;
    return 0;
}

私が知りたいのは次のことです。

  • これについてもっと良い方法はありますか?継承および合成された属性、ローカル変数、およびフェニックス ブードゥー教を少し使用しました。これは素晴らしい; ツールを学ぶのは良いことですが、同じことを達成するもっと簡単な方法があるかもしれないと考えずにはいられません:/ (PEGパーサー内で...)
  • たとえば、ローカル変数なしでどのように行うことができますか?

詳細情報:同時に解析される他のデータ形式があるため、戻り値をパーサー属性として保持したいと考えています。現時点では、これは std::pair であり、解析時に他のデータ形式は、たとえば独自の std::pair を公開します。これらを std::vector に詰め込みたいと思います。

4

2 に答える 2

2

これは少なくともすでにかなり短いです:

  • 28LOCまで
  • これ以上地元の人はいない
  • これ以上の融合ベクトルのat<>魔法使いはありません
  • 継承された属性はもうありません
  • これ以上の文法クラスはありません
  • これ以上の手動反復はありません
  • 期待値(を参照other)を使用して、解析エラーレポートを強化します
  • このパーサー式は、vector<int>を割り当てることを選択した場合、きちんと合成されてaになります%=(ただし、大きな配列を割り当てる可能性があることに加えて、パフォーマンスが低下します)

#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>

namespace px = boost::phoenix;
namespace qi = boost::spirit::qi;

typedef std::string::iterator It;

int main(int argc, char** argv)
{
    std::string input("11 0.1\n"
            "14 0.78\n"
            "532 -3.6\n");

    int min=-1, max=0;
    {
        using namespace qi;
        using px::val;
        using px::ref;

        It begin(input.begin()), end(input.end());
        rule<It> index = int_ 
            [
                if_(ref(max) < _1)  [ ref(max) = _1 ] .else_ [ std::cout << _1 << val(" out of order\n") ],
                if_(ref(min) <  0)  [ ref(min) = _1 ]
            ] ;

        rule<It> other = char_(" \t") > float_ > eol;

        std::cout << "parse result: " << std::boolalpha 
                  << qi::parse(begin, end, index % other) << std::endl; 
    }
    std::cout << "min: " << min << "\nmax: " << max << std::endl;
    return 0;
}

ボーナス

式から検証を取り除き、それを独立した関数にすることをお勧めします。もちろん、これは物事をより冗長にし(そして...読みやすく)、私の頭の悪いサンプルはグローバル変数を使用します...-しかし、あなたがそれをより現実的にする方法を知っていると信じていますboost::bindpx::bind

上記に加えて

  • フリー機能でも27LOCまで
  • これ以上のフェニックス、これ以上のフェニックスには含まれていません(yayコンパイル時)
  • デバッグビルドのフェニックス式タイプがバイナリを膨らませて遅くすることはもうありません
  • これ以上var、、、refおよび惨めなもの( phoenix.hppに含まれていない過負荷のために(ある時点で)大きなバグのリスクがif_ありました).else_operator,
  • c ++ 0xラムダに簡単に移植できます-グローバル変数の必要性をすぐに取り除きます

#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
namespace px = boost::phoenix;
namespace qi = boost::spirit::qi;
typedef std::string::iterator It;

int min=-1, max=0, linenumber=0;
void validate_index(int index)
{
    linenumber++;
    if (min < 0)     min = index;
    if (max < index) max = index;
    else             std::cout << index << " out of order at line " << linenumber << std::endl;
}

int main(int argc, char** argv)
{
    std::string input("11 0.1\n"
            "14 0.78\n"
            "532 -3.6\n");
    It begin(input.begin()), end(input.end());

    {
        using namespace qi;

        rule<It> index = int_ [ validate_index ] ;
        rule<It> other = char_(" \t") > float_ > eol;
        std::cout << "parse result: " << std::boolalpha 
                  << qi::parse(begin, end, index % other) << std::endl; 
    }
    std::cout << "min: " << min << "\nmax: " << max << std::endl;
    return 0;
}
于 2011-06-23T07:30:50.883 に答える
2

はるかに簡単な方法は、標準のストリーム操作を使用してファイルを解析し、ループで順序を確認することだと思います。まず、入力:

typedef std::pair<int, float> value_pair;

bool greater(const value_pair & left, const value_pair & right) {
    return left.first > right.first;
}

std::istream & operator>>(std::istream & stream, value_pair & value) {
    stream >> value.first >> value.second;
    return stream;
}

次のように使用します。

std::ifstream file("your_file.txt");
std::istream_iterator<value_pair> it(file);
std::istream_iterator<value_pair> eof;

if(std::adjacent_find(it, eof, greater) != eof) {
    std::cout << "The values are not ordered" << std::endl;
}

これはずっと簡単だと思います。

于 2011-06-23T07:05:13.807 に答える