在Perl中为XML或XHTML DOM进行Regex解析



我知道这看起来像火焰诱饵,但事实并非如此。听我说完。因为stackexchange更喜欢问题(这主要是一个答案),所以让我问"下面的问题出了什么问题?">

Regex不适合DOM解析。然而,当可用时,与复杂的DOM解析器相比,它们具有良好的可推广性、易用性,并且没有额外的学习曲线。

因此,我想分享一个技巧,让regex适合快速而肮脏的DOM更改。它将临时gid添加到html标记及其关闭属性中,然后使用regex反向引用。

幸运的是,代码很短,并且符合perl的一种哲学,即如果某些解决方案的编程时间比运行时间短,那么它们可能会更好。

#!/usr/bin/perl -w
use strict;
use warnings FATAL => qw{ uninitialized };
################################################################
sub tokenize {
my $GLOBAL; (defined($_[0])) and do { $GLOBAL = $_; $_ = $_[0]; };
my @xmlstack;
my $gid=0;
my $addgid= sub {
my ($s,$e) = @_;
($e =~ //$/) and return "<$s$e/>";  ## could add gid here, too.
if ($s =~ /^/(.*)/) {
my $off= pop(@xmlstack);
($off->[0] eq $1) or die "not a valid document at id=$gid. (wanted = $off->[0] . had = $s).n";
return "<$s gid="".($off->[1])."">"; ## not legal html now, but easy to remove
} else {
push(@xmlstack, [$s, ++$gid]);
return "<$s gid="$gid">";
}
};
my $U="#!#";
(/$U/) and die "sorry, this is a hack.  pick any other unique string than '$U'n";
s/<!--(.*?)-->/$U$1$U/gms;  # comments can contain html tags
s/<(/?[a-zA-Z0-9]*)(.*?)>/$addgid->($1,$2)/emsg;
s/$U(.*?)$U/<!--$1-->/gms;
(@xmlstack) and die "unfinished business: ".pop(@xmlstack)->[0]."n";
if ($GLOBAL) { my $CHANGED= $_; $_ = $GLOBAL; return $CHANGED; } else { return $_; }
}
sub untokenize { 
my $GLOBAL; (defined($_[0])) and do { $GLOBAL = $_; $_ = $_[0]; };
s/ gid="[0-9]+">/>/g; ## buglet: could mistakenly remove gid from inside comments.
if ($GLOBAL) { my $CHANGED= $_; $_ = $GLOBAL; return $CHANGED; } else { return $_; }
}
################################################################

$_ = "<html>n<body>n
<p> <sup>a</sup><sub>b</sub>. </p>.
<hr />
<p> hi<sup>u<sub>ud<sup>udu</sup></sub></sup> </p>.
</body>
</html>
";

tokenize();
## now we can use regex using backreferences
while (/<sup (gid="[0-9]+")>(.*)</sup g1>/gms) {
print "Example matching of all sup's:  $1 $2n";  ## could call recursively
}
## another example:  add a class to sup that is immediately followed by a sub
s/<sup (gid="[0-9]+")>(.*)</sup g1>s*<sub/<sup class="followed" $1>$2</sup $1><sup/gms;
print untokenize($_);

这可能还不知道HTML的复杂性,但它可以处理许多DOM xhtml和xml作业,否则就不适合regex解析。

我发布的解决方案很幼稚。

加:

  • 在随机页面上,它似乎可以处理互联网上10个xhtml网页中的9个。它可以处理普通的栈xhtml文件,但可能会在更不寻常的功能(如DTD等)上失败。如果另一个程序生成了你的xhtml输出,它可能会一直工作。

  • 与真实的DOM解析相比,这里的学习曲线大约是1/10

  • 与真实DOM解析相比,这里的代码大约是其大小的1/10。

  • 然后可以使用熟悉的perlregex知识。

  • 请注意,这个工具相当有限。如果您超出了它的能力,那么您可能必须学习一个更好的DOM解析器。

减:

  • 如果需要完美的DOM解析,这是完全不合适的。这个代码是可破解的。它遵循的是伯克希尔而不是at&t方法。

  • 但是,完美的DOM解析器也可能在糟糕的HTML文档上失败。

  • 如果您已经知道DOM解析,那么做对它几乎没有时间成本。使用Mojolique或XML::LibXML。那么你不妨坚持更好的解决方案。。

给这个代码一个反身-1投票会忽略它的用途。有时,一把普通的螺丝刀可以做一把飞利浦更好的工作。这个代码是一把普通的十字螺丝刀。stackoverflow是一个新手也需要快速解决方案的网站;不仅仅是专家。这就是我发布它的原因。

简单的改进修复是值得赞赏的,尽管这里的目标明确不是处理xml和xhtml的所有可能的有效和无效、理智和疯狂、正确和不正确的排列。

/iaw

最新更新