4

これが取引です。複数の正規表現に基づいて行で文字列をトークン化する方法はありますか?

一例:

すべての href タグ、対応するテキスト、および別の正規表現に基づくその他のテキストを取得する必要があります。したがって、3 つの式があり、行をトークン化し、すべての式に一致するテキストのトークンを抽出したいと考えています。

私は実際に、古き良き lex の実装である flex (Adobe と混同しないでください) を使用してこれを行いました。lex は、式に基づいて「アクション」を実行することにより、これを行うエレガントな方法を提供します。lex がファイルを読み取る方法も制御できます (ブロック/行ベースの読み取り)。

問題は、flex が実際にトークン化ジョブを実行する C/C++ コードを実際に生成することです。これらすべてをラップするmakeファイルがあります。perl /python が何らかの方法で同じことをできるかどうか疑問に思っていました。好きなことをすべて単一のプログラミング言語で行いたいというだけです。

トークン化は、アプリケーションの一部としてやりたいことの 1 つにすぎません。

perl または python とは別に、どの言語 (関数型も) でこれを行うことができますか?

ここでPLYとANTLRについて読みました(解析、どこで学べますか)。

しかし、Python自体で自然に行う方法はありますか? 無知で申し訳ありませんが、これらのツールは一般的な製品やサービスで使用されていますか?

ありがとうございました。

4

8 に答える 8

8

CPANの次のモジュールのドキュメントを参照してください

HTML::ツリービルダー

HTML::TableExtract

パース::RecDescent

これらのモジュールを使用して、非常に大きく複雑な Web ページを処理しました。

于 2008-10-02T04:53:03.830 に答える
7

特にWebページからリンクを解析した後であれば、PerlのWWW::Mechanizeモジュールが非常にエレガントな方法で物事を理解します。これは、Stack Overflowの最初のページを取得し、すべてのリンクを解析して、テキストと対応するURLを出力するサンプルプログラムです。

#!/usr/bin/perl
use strict;
use warnings;
use WWW::Mechanize;

my $mech = WWW::Mechanize->new;

$mech->get("http://stackoverflow.com/");

$mech->success or die "Oh no! Couldn't fetch stackoverflow.com";

foreach my $link ($mech->links) {
    print "* [",$link->text, "] points to ", $link->url, "\n";
}

メインループでは、それぞれ$linkWWW :: Mechanize :: Linkオブジェクトであるため、テキストとURLの取得に制限されるだけではありません。

ではごきげんよう、

ポール

于 2008-10-02T06:52:06.337 に答える
5

あなたは本当に HTML を解析したいだけのように思えますが、そうするために素晴らしいパッケージのいずれかを見ることをお勧めします:

または!次のいずれかのようなパーサーを使用できます。

  • PyParsing
  • DParser - 優れた Python バインディングを備えた GLR パーサー.
  • ANTLR - Python コードを生成できる再帰的パーサージェネレータ.

この例は BeautifulSoup Documentationからのものです:

from BeautifulSoup import BeautifulSoup, SoupStrainer
import re

links = SoupStrainer('a')
[tag for tag in BeautifulSoup(doc, parseOnlyThese=links)]
# [<a href="http://www.bob.com/">success</a>, 
#  <a href="http://www.bob.com/plasma">experiments</a>, 
#  <a href="http://www.boogabooga.net/">BoogaBooga</a>]

linksToBob = SoupStrainer('a', href=re.compile('bob.com/'))
[tag for tag in BeautifulSoup(doc, parseOnlyThese=linksToBob)]
# [<a href="http://www.bob.com/">success</a>, 
#  <a href="http://www.bob.com/plasma">experiments</a>]
于 2008-10-02T04:38:26.860 に答える
3

PyParsingを見たことがありますか?

彼らのホームページから:

これは、「Hello, World!」を解析するプログラムです。(または「, !」の形式の挨拶):

from pyparsing import Word, alphas
greet = Word( alphas ) + "," + Word( alphas ) + "!" # <-- grammar defined here
hello = "Hello, World!"
print hello, "->", greet.parseString( hello )

プログラムは次を出力します。

Hello, World! -> ['Hello', ',', 'World', '!']
于 2008-10-02T04:37:53.037 に答える
2

問題が Web スクレイピングに関係している場合は、XPath または CSS セレクターを介して簡単に要素を選択できるWeb::Scraperを検討することをお勧めします。Web::Scraper に関する(ドイツ語の)講演がありますが、babelfish で実行するか、コード サンプルを見るだけで、構文の概要を簡単に把握できます。

HTML を手動で解析するのは面倒であり、既製の HTML パーサーのいずれかを使用するよりも多くのことを行うことはできません。HTML のバリエーションが非常に限られている場合は、巧妙な正規表現を使用することで対処できますが、ハードコアなパーサー ツールを既に開発している場合は、HTML が適切に解析できるものよりもはるかに規則的であるかのように聞こえます。正規表現。

于 2008-10-02T08:37:58.593 に答える
2

また、この種のことを行うための本当に素晴らしいPerlishの方法として、pQueryをチェックしてください....

use pQuery;

pQuery( 'http://www.perl.com' )->find( 'a' )->each( 
    sub {
        my $pQ = pQuery( $_ ); 
        say $pQ->text, ' -> ', $pQ->toHtml;
    }
);

# prints all HTML anchors on www.perl.com
# =>  link text -> anchor HTML

ただし、要件が HTML/Web を超えている場合は、以前の "Hello World!" を使用してください。Parse::RecDescentの例...

use strict;
use warnings;
use Parse::RecDescent;

my $grammar = q{
    alpha : /\w+/
    sep   : /,|\s/
    end   : '!'
    greet : alpha sep alpha end { shift @item; return \@item }
};

my $parse = Parse::RecDescent->new( $grammar );
my $hello = "Hello, World!";
print "$hello -> @{ $parse->greet( $hello ) }";

# => Hello, World! -> Hello , World !

おそらく、このナットを割るには大きなハンマーが多すぎます ;-)

于 2008-10-02T12:19:37.373 に答える
1

perlopから :

lex ライクなスキャナの便利なイディオムは/\G.../gc. このようないくつかの正規表現を組み合わせて、文字列を部分ごとに処理し、一致した正規表現に応じて異なるアクションを実行できます。各正規表現は、前の正規表現が終了した場所と一致しようとします。

 LOOP:
    {
      print(" digits"),       redo LOOP if /\G\d+\b[,.;]?\s*/gc;
      print(" lowercase"),    redo LOOP if /\G[a-z]+\b[,.;]?\s*/gc;
      print(" UPPERCASE"),    redo LOOP if /\G[A-Z]+\b[,.;]?\s*/gc;
      print(" Capitalized"),  redo LOOP if /\G[A-Z][a-z]+\b[,.;]?\s*/gc;
      print(" MiXeD"),        redo LOOP if /\G[A-Za-z]+\b[,.;]?\s*/gc;
      print(" alphanumeric"), redo LOOP if /\G[A-Za-z0-9]+\b[,.;]?\s*/gc;
      print(" line-noise"),   redo LOOP if /\G[^A-Za-z0-9]+/gc;
      print ". That's all!\n";
    }
于 2008-10-02T13:36:36.533 に答える
0

Bruno の例を修正して、エラー チェックを含めます。

my $input = "...";
while (1) {
    if ($input =~ /\G(\w+)/gc) { print "word: '$1'\n"; next }
    if ($input =~ /\G(\s+)/gc) { print "whitespace: '$1'\n"; next }

    if ($input !~ /\G\z/gc)  { print "tokenizing error at character " . pos($input) . "\n" }
    print "done!\n"; last;
}

(残念ながら、スカラー //g の使用は、$1 などの変数の使用を避けることができない唯一の場所であることに注意してください。)

于 2008-10-05T18:07:23.407 に答える