4

这是交易。有没有办法根据多个正则表达式在一行中对字符串进行标记?

一个例子:

我必须根据不同的正则表达式获取所有 href 标记、它们对应的文本和其他一些文本。所以我有 3 个表达式,并且想对行进行标记并提取与每个表达式匹配的文本标记。

我实际上是使用 flex 完成的(不要与 Adob​​e 混淆),它是旧 lex 的一个实现。lex 通过基于表达式执行“动作”提供了一种优雅的方式来做到这一点。也可以控制 lex 读取文件的方式(基于块/行的读取)。

问题是 flex 实际上生成了实际执行标记化工作的 C/C++ 代码。我有一个包含所有这些东西的 make 文件。我想知道 perl /python 是否可以以某种方式做同样的事情。只是我想用一种编程语言本身做我喜欢的一切。

标记化只是我想要在我的应用程序中做的事情之一。

除了 perl 或 python 之外,任何语言(也可以是函数式)都可以做到这一点吗?

我确实在这里阅读了 PLY 和 ANTLR(解析,我在哪里可以了解它)。

但是有没有办法在 python 本身中自然地做到这一点?请原谅我的无知,但这些工具是否用于任何流行的产品/服务?

谢谢你。

4

8 回答 8

8

在CPAN上查看以下模块的文档

HTML::TreeBuilder

HTML::表格提取

Parse::RecDescent

我已经使用这些模块来处理相当大和复杂的网页。

于 2008-10-02T04:53:03.830 回答
7

如果您专门从网页中解析链接,那么 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";
}

在主循环中,每个$link都是WWW::Mechanize::Link对象,因此您不仅限于获取文本和 URL。

祝一切顺利,

保罗

于 2008-10-02T06:52:06.337 回答
5

听起来您真的只是想解析 HTML,我建议您查看任何出色的软件包:

或者!您可以使用类似以下之一的解析器:

  • PyParsing
  • DParser - 具有良好 python 绑定的 GLR 解析器。
  • ANTLR - 可以生成 python 代码的递归体面解析器生成器。

此示例来自 BeautifulSoup文档

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::Scraper,它通过 XPath 和 CSS 选择器提供了简单的元素选择。我在 Web::Scraper 上有一个(德语)演讲,但是如果你通过 babelfish 运行它或者只是查看代码示例,这可以帮助你快速了解语法。

手动解析 HTML 是繁重的,并且不会让您过度使用预制的 HTML 解析器之一。如果你的 HTML 的变化非常有限,你可以通过使用聪明的正则表达式来解决,但如果你已经在使用核心解析器工具,听起来你的 HTML 比正常解析的要规则得多常用表达。

于 2008-10-02T08:37:58.593 回答
2

还可以查看pQuery它是一种非常好的 Perlish 方式来做这种事情......

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 回答