提问人:prabhu 提问时间:10/2/2008 最后编辑:Communityprabhu 更新时间:4/21/2017 访问量:1526
在 Perl 或 Python 中模拟类似 lex 的功能
Emulation of lex like functionality in Perl or Python
问:
这是交易。有没有办法根据多个正则表达式在一行中标记字符串?
举个例子:
我必须获取所有 href 标签、它们的相应文本以及基于不同正则表达式的其他一些文本。 所以我有 3 个表达式,想对行进行标记并提取与每个表达式匹配的文本标记。
我实际上已经使用 flex(不要与 Adobe 混淆)完成了此操作,这是旧 lex 的实现。Lex 提供 通过执行基于表达式的“操作”来做到这一点的一种优雅方式。一个人可以控制方式 Lex 也读取文件(基于块/行的读取)。
问题是flex实际上生成了C / C++代码,这些代码实际上执行了标记化工作。我有一个 make 文件,它包装了所有这些东西。 我想知道perl / python是否可以以某种方式做同样的事情。只是我想做任何事情 我喜欢单一的编程语言本身。
标记化只是我想在我的应用程序中做的事情之一。
除了perl或python之外,任何语言(函数式)都可以做到这一点吗?
我确实在这里阅读了有关 PLY 和 ANTLR 的信息(解析,我在哪里可以了解它)。
但是有没有办法在python本身中自然地做到这一点呢?请原谅我的无知,但这些工具是否用于任何流行的产品/服务?
谢谢。
答:
你看过 PyParse 吗?
从他们的主页:
这是一个解析“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', '!']
评论
听起来你真的只想解析 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>]
查看有关CPAN的以下模块的文档
和
我使用这些模块来处理相当大和复杂的网页。
如果你专门想从网页中解析链接,那么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";
}
在主循环中,每个都是一个 WWW::Mechanize::Link 对象,因此您不仅仅局限于获取文本和 URL。$link
祝一切顺利,
保罗
评论
如果您的问题与网页抓取有关,我建议您查看 Web::Scraper ,它通过 XPath 和 CSS 选择器提供简单的元素选择。我在 Web::Scraper 上有一个(德语)演讲,但如果您通过 babelfish 运行它或仅查看代码示例,这可以帮助您快速了解语法。
手动解析 HTML 很繁重,并且不会给您带来太多使用预制 HTML 解析器之一。如果你的 HTML 变化非常有限,你可以通过使用聪明的正则表达式来解决这个问题,但如果你已经突破了硬核解析器工具,听起来好像你的 HTML 比使用正则表达式解析的要规则得多。
另外,请查看 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,那么这里是 Parse::RecDescent 中较早的“Hello World!” 示例。
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 !
可能太大的锤子来敲这个螺母;-)
来自 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"; }
修改 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 等变量的地方。
评论