1

そのため、多くのコードで正規表現の使用を置き換えるために、Boost.Spiritについて少し学びました。主な理由は純粋な速度です。Boost.Spiritは、いくつかの比較的単純なタスクでPCREよりも最大50倍高速であることがわかりました。

私のアプリの1つで大きなボトルネックになっているのは、HTMLを取得し、すべての「img」タグを見つけて、「src」属性を抽出することです。

これは私の現在の正規表現です:

(?i:<img\s[^\>]*src\s*=\s*[""']([^<][^""']+)[^\>]*\s*/*>)

私はSpiritで何かを機能させるためにそれをいじってみましたが、これまでのところ私は空っぽになっています。この正規表現と同じことを達成する一連のSpiritルールを作成する方法に関するヒントは素晴らしいでしょう。

4

3 に答える 3

2

そしてもちろん、ブーストスピリットのバリエーションも見逃せません。

sehe@natty:/tmp$ time ./spirit < bench > /dev/null

real    0m3.895s
user    0m3.820s
sys 0m0.070s

正直なところ、Spiritコードは他のバリエーションよりもわずかに用途が広いです。

  • 実際には属性を少し賢く解析するので、おそらく含まれている要素に応じて、さまざまな属性を同時に処理するのは簡単です。
  • スピリットパーサーは、クロスラインマッチングに適応するのが簡単です。これは最も簡単に達成できます

    • 使用spirit::istream_iterator<>(残念ながら悪名高いほど遅い)
    • const char*イテレータとしてrawを使用するメモリマップトファイルを使用する。後者のアプローチは、他の手法でも同様に機能します

コードは次のとおりです:(完全なコードはhttps://gist.github.com/c16725584493b021ba5bにあります)

//#define BOOST_SPIRIT_DEBUG
#include <string>
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

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

void handle_attr(
        const std::string& elem, 
        const std::string& attr, 
        const std::string& value)
{
    if (elem == "img" && attr == "src")
        std::cout << "value : " << value << std::endl;
}

typedef std::string::const_iterator It;
typedef qi::space_type Skipper;

struct grammar : qi::grammar<It, Skipper>
{
    grammar() : grammar::base_type(html)
    {
        using namespace boost::spirit::qi;
        using phx::bind;

        attr = as_string [ +~char_("= \t\r\n/>") ] [ _a = _1 ]
                >> '=' >> (
                    as_string [ '"' >> lexeme [ *~char_('"') ] >> '"' ]
                  | as_string [ "'" >> lexeme [ *~char_("'") ] >> "'" ]
                  ) [ bind(handle_attr, _r1, _a, _1) ]
            ;

        elem = lit('<') 
            >> as_string [ lexeme [ ~char_("-/>") >> *(char_ - space - char_("/>")) ] ] [ _a = _1 ]
            >> *attr(_a);

        html = (-elem) % +("</" | (char_ - '<'));

        BOOST_SPIRIT_DEBUG_NODE(html);
        BOOST_SPIRIT_DEBUG_NODE(elem);
        BOOST_SPIRIT_DEBUG_NODE(attr);
    }

    qi::rule<It, Skipper> html;
    qi::rule<It, Skipper, qi::locals<std::string> > elem;
    qi::rule<It, qi::unused_type(std::string), Skipper, qi::locals<std::string> > attr;
};

int main(int argc, const char *argv[])
{
    std::string s;

    const static grammar html_;

    while (std::getline(std::cin, s))
    {
        It f = s.begin(),
           l = s.end();

        if (!phrase_parse(f, l, html_, qi::space) || (f!=l))
            std::cerr << "unparsed: " << std::string(f,l) << std::endl;
    }

    return 0;
}
于 2011-12-14T15:10:01.277 に答える
1

アップデート

ベンチマークを行いました。

完全な開示はここにあります:https ://gist.github.com/c16725584493b021ba5b

これには、使用される完全なコード、コンパイルフラグ、および使用されるテストデータ(ファイルbench)の本体が含まれます。

要するに

  • ここでは、正規表現は確かに高速で簡単です。
  • 正しくするためにSpirit文法のデバッグに費やした時間を過小評価しないでください!
  • 「偶発的な」違いを排除するために注意が払われています(例えば
    • ほとんどの場合、 Spirithandle_attributeの実装でのみ意味がありますが、実装全体で変更を加えないでください。
    • 両方に同じ行単位の入力スタイル文字列イテレータを使用する
  • 現在、3つの実装すべてでまったく同じ出力が得られます
  • g ++ 4.6.1(c ++ 03モード)、-O3でビルド/タイミング設定されたものすべて

正規表現を使用してHTMLを解析するべきではないというひざまずく(そして正しい)応答への応答を編集します。

  • 自明でない入力を解析するために正規表現を使用するべきではありません(主に、文法を含むもの。もちろん、Perl 5.10+の「正規表現文法」は例外です。これは、正規表現が分離されていないためです。
  • HTMLは基本的に解析できず、非標準のタグスープです。厳密な(X)HTMLは別の問題です
  • Xaadeによると、標準に準拠したHTMLリーダーを使用して完全な実装を作成するのに十分な時間がない場合は、

「彼らがたわごとをしたいかどうかクライアントに尋ねなさい。彼らがたわごとをしたいなら、あなたは彼らにもっと請求する。たわごとは彼らよりあなたに多くの費用がかかる。」--Xaade _

とはいえ、ここで提案することを正確に実行するシナリオがあります。正規表現を使用します。主に、1回限りのクイック検索を実行する場合、または既知のデータなどの大まかな統計を毎日取得する場合は、YMMVを使用して、独自の電話をかける必要があります。

タイミングと概要については、以下を参照してください。

  • 以下の正規表現の回答をブーストします
  • ここでXpressiveの答えを後押し
  • ここでスピリットの答え

ここで正規表現を使用することを心からお勧めします。

typedef std::string::const_iterator It;

int main(int argc, const char *argv[])
{
    const boost::regex re("<img\\s+[^\\>]*?src\\s*=\\s*([\"'])(.*?)\\1");

    std::string s;
    boost::smatch what;

    while (std::getline(std::cin, s))
    {
        It f = s.begin(), l = s.end();

        do
        {
            if (!boost::regex_search(f, l, what, re))
                break;

            handle_attr("img", "src", what[2]);
            f = what[0].second;
        } while (f!=s.end());
    }
    
    return 0;
}

次のように使用します。

./test < index.htm

スピリットベースのアプローチがもっと速くなるべき/できた理由がわかりませんか?

PSを編集します。静的最適化が鍵になると主張する場合は、それをBoost Expressive、静的、正規表現に変換してみませんか?

于 2011-12-14T11:01:27.147 に答える
1

好奇心から、静的にコンパイルされた正規表現を使用して、BoostXpressiveに基づいて正規表現サンプルを再作成しました。

sehe@natty:/tmp$ time ./expressive < bench > /dev/null

real    0m2.146s
user    0m2.110s
sys 0m0.030s

興味深いことに、動的正規表現を使用する場合、認識できる速度の違いはありません。ただし、全体として、XpressiveバージョンはBoost Regexバージョンよりもパフォーマンスが優れています(約10%)

本当に素晴らしいのは、IMOですが、xpressive.hppBoost RegexからXpressiveに変更するために、いくつかの名前空間を含めて変更することがほとんど問題でした。APIインターフェース(使用されている限り)はまったく同じです。

関連するコードは次のとおりです:(完全なコードはhttps://gist.github.com/c16725584493b021ba5bにあります)

typedef std::string::const_iterator It;

int main(int argc, const char *argv[])
{
    using namespace boost::xpressive;
#if DYNAMIC
    const sregex re = sregex::compile
         ("<img\\s+[^\\>]*?src\\s*=\\s*([\"'])(.*?)\\1");
#else
    const sregex re = "<img" >> +_s >> -*(~(set = '\\','>')) >> 
        "src" >> *_s >> '=' >> *_s
        >> (s1 = as_xpr('"') | '\'') >> (s2 = -*_) >> s1;
#endif

    std::string s;
    smatch what;

    while (std::getline(std::cin, s))
    {
        It f = s.begin(), l = s.end();

        do
        {
            if (!regex_search(f, l, what, re))
                break;

            handle_attr("img", "src", what[2]);
            f = what[0].second;
        } while (f!=s.end());
    }

    return 0;
}
于 2011-12-14T15:06:03.857 に答える