如果我有这样的HTML:
<b>1<i>2</i>3</b>
以下正则表达式:
\<[^\>\/]+\>(.*?)\<\/[^\>]+\>
然后它将匹配:
<b>1<i>2</i>
我希望它只匹配开始和结束标记相同的HTML。有没有办法做到这一点?
谢谢,
乔
答案 0 :(得分:6)
有办法做到这一点吗?
是的,当然。忽略那些轻率的非答案,告诉你它无法完成。它当然可以。您可能不希望这样做,正如我在下面解释的那样。
假装HTML <i>
和<b>
标记的随机数总是被剥夺属性,而且,既不重叠也不嵌套,我们有这个简单的解决方案:
#!/usr/bin/env perl
#
# solution A: numbered captures
#
use v5.10;
while (<>) {
say "$1: $2" while m{
< ( [ib] ) >
(
(?:
(?! < /? \1 > ) .
) *
)
</ \1 >
}gsix;
}
运行时,产生这个:
$ echo 'i got <i>foo</i> and <b>bar</b> bits go here' | perl solution-A
i: foo
b: bar
最好使用命名捕获,这导致了这种等效的解决方案:
#!/usr/bin/env perl
#
# Solution B: named captures
#
use v5.10;
while (<>) {
say "$+{name}: $+{contents}" while m{
< (?<name> [ib] ) >
(?<contents>
(?:
(?! < /? \k<name> > ) .
) *
)
</ \k<name> >
}gsix;
}
当然,假设这样的标签既不重叠也不嵌套是不合理的。由于这是递归数据,因此需要递归模式来解决。记住用于递归地解析嵌套parens的trival模式只是:
( \( (?: [^()]++ | (?-1) )*+ \) )
我将在前面的解决方案中构建这种递归匹配,并且我还会进行一些交互式处理来解开内部位。
#!/usr/bin/perl
use v5.10;
# Solution C: recursive captures, plus bonus iteration
while (my $line = <>) {
my @input = ( $line );
while (@input) {
my $cur = shift @input;
while ($cur =~ m{
< (?<name> [ib] ) >
(?<contents>
(?:
[^<]++
| (?0)
| (?! </ \k<name> > )
.
) *+
)
</ \k<name> >
}gsix)
{
say "$+{name}: $+{contents}";
push @input, $+{contents};
}
}
}
当demo'd产生这个时:
$ echo 'i got <i>foo <i>nested</i> and <b>bar</b> bits</i> go here' | perl Solution-C
i: foo <i>nested</i> and <b>bar</b> bits
i: nested
b: bar
这仍然相当简单,所以如果它适用于你的数据,那就去吧。
但是,它实际上并不知道正确的HTML语法,它允许将标记属性添加到<i>
和<b>
之类的内容中。
正如this answer中所解释的那样,人们当然可以使用正则表达式来解析标记语言,前提是要注意它。
例如,这知道与<i>
(或<b>
)标记密切相关的属性。在这里,我们定义了用于构建语法正则表达式的正则表达式子例程。这些只是定义,就像定义常规潜艇一样,但现在用于正则表达式:
(?(DEFINE) # begin regex subroutine defs for grammatical regex
(?<i_tag_end> < / i > )
(?<i_tag_start> < i (?&attributes) > )
(?<attributes> (?: \s* (?&one_attribute) ) *)
(?<one_attribute>
\b
(?&legal_attribute)
\s* = \s*
(?:
(?"ed_value)
| (?&unquoted_value)
)
)
(?<legal_attribute>
(?&standard_attribute)
| (?&event_attribute)
)
(?<standard_attribute>
class
| dir
| ltr
| id
| lang
| style
| title
| xml:lang
)
# NB: The white space in string literals
# below DOES NOT COUNT! It's just
# there for legibility.
(?<event_attribute>
on click
| on dbl click
| on mouse down
| on mouse move
| on mouse out
| on mouse over
| on mouse up
| on key down
| on key press
| on key up
)
(?<nv_pair> (?&name) (?&equals) (?&value) )
(?<name> \b (?= \pL ) [\w\-] + (?<= \pL ) \b )
(?<equals> (?&might_white) = (?&might_white) )
(?<value> (?"ed_value) | (?&unquoted_value) )
(?<unwhite_chunk> (?: (?! > ) \S ) + )
(?<unquoted_value> [\w\-] * )
(?<might_white> \s * )
(?<quoted_value>
(?<quote> ["'] )
(?: (?! \k<quote> ) . ) *
\k<quote>
)
(?<start_tag> < (?&might_white) )
(?<end_tag>
(?&might_white)
(?: (?&html_end_tag)
| (?&xhtml_end_tag)
)
)
(?<html_end_tag> > )
(?<xhtml_end_tag> / > )
)
一旦你的语法部分被组合起来,你就可以将这些定义纳入已经给出的递归解决方案,以便做得更好。
然而,仍然有一些事情没有被考虑过,而且在更一般的情况下必须是这样。这些已经在the longer solution已经提供过。
我只能想到三个可能的原因,为什么你可能不关心使用正则表达式来解析一般的HTML:
其中任何一个或多个都可能适用。在这种情况下,不要这样做。
对于简单的罐装示例,这条路线很容易。你希望它能够在你以前从未见过的事情上变得越强大,这条路线就越难。
如果你使用像Python这样的语言,甚至更糟糕的Javascript,使用低劣的,贫困的模式匹配,你当然不能做任何事情。那些几乎没有比Unix grep
程序好,在某些方面,甚至更糟。不,你需要一个现代模式匹配引擎,比如Perl或PHP中的引擎,甚至可以从这条路开始。
但老实说,让别人为你做这件事可能更容易,我的意思是你应该使用已经编写过的解析模块。
仍然,理解为什么不打扰这些基于正则表达式的方法(至少,不超过一次)要求您首先使用正则表达式正确实现正确的HTML解析。你需要了解它的全部意义。因此,像这样的小练习对于提高对问题空间的整体理解以及现代模式匹配的整体理解非常有用。
这个论坛并不是用于解释现代模式匹配所有这些内容的正确格式。但是,有些书籍表现得非常好。
答案 1 :(得分:-1)
您可能不希望将正则表达式与HTML结合使用。
但如果您仍想这样做,则需要查看backreferences。
基本上它是一种捕获组(例如“b”或“i”)的方法,以便稍后在同一个正则表达式中使用它。
相关问题: