【问题标题】:Match nested html comment blocks regular expression [duplicate]匹配嵌套的html注释块正则表达式[重复]
【发布时间】:2014-07-24 12:00:50
【问题描述】:

我有这个 html 代码块:

some html content here top base
<!--block:first-->
    some html content here 1 top
    <!--block:second-->
        some html content here 2 top
        <!--block:third-->
            some html content here 3a
            some html content here 3b
        <!--endblock-->
        some html content here 2 bottom
    <!--endblock-->
    some html content here 1 bottom
<!--endblock-->
some html content here bottom base

我得到了这个正则表达式来匹配嵌套块:

/(?is)<!--block:(.*?)-->((?:(?:(?!<!--(?:.*?)-->).)|(?R))*?)<!--endblock-->|((?:(?!<!--.*?-->).)+)/g

这可以正常工作,但如果任何块内容中有 cmets,则会中断。

这将失败,因为 &lt;!--comment--&gt; 仅在第一场比赛中,但其余比赛将正常工作:

<!--block:first-->
    some html content here 1 top
    this <!--comment--> will make it fail here.
    <!--block:second-->
        some html content here 2 top
        <!--block:third-->
            some html content here 3a
            some html content here 3b
        <!--endblock-->
        some html content here 2 bottom
    <!--endblock-->
    some html content here 1 bottom
<!--endblock-->
some html content here bottom base

这是对此question 的跟进。

下面的 Perl 测试代码:

use Data::Dumper;

$/ = undef;
my $content = <DATA>;


my %blocks = ();
$blocks{'base'} = [];


ParseCore( $blocks{'base'}, $content );


sub ParseCore
{
    my ($aref, $core) = @_;
    while ( $core =~ /(?is)<!--block:(.*?)-->((?:(?:(?!<!--(?:.*?)-->).)|(?R))*?)<!--endblock-->|((?:(?!<!--.*?-->).)+)/g )
    {
        if ( defined $1 )
        {
           my $branch = {};
           push @{$aref}, $branch;
           $branch->{$1} = [];
           ParseCore( $branch->{$1}, $2 );
        }
        elsif ( defined $3 )
        {
           push @{$aref}, $3;
        }
    }

}

print Dumper(\%blocks);

__DATA__

some html content here top base
<!--block:first-->
    some html content here 1 top
    this <!--comment--> will make it fail here.
    <!--block:second-->
        some html content here 2 top
        <!--block:third-->
            some html content here 3a
            some html content here 3b
        <!--endblock-->
        some html content here 2 bottom
    <!--endblock-->
    some html content here 1 bottom
<!--endblock-->
some html content here bottom base

【问题讨论】:

  • Mojo::DOM 可以找到 cmets,不会强迫您浪费时间摆弄脆弱的正则表达式。
  • 我必须使用正则表达式来满足我的确切需求。
  • @daliaessam:好吧,我会咬的。正则表达式可以满足哪些特殊需求,而适当的 HTML 解析器却不能?
  • 在这种情况下,您只需要更精确(必须定义)什么样的块可以是“自关闭注释块”以及什么样的块始终具有 html 标签的行为(需要一个结束标签)。
  • @daliaessam 你的任务是如此的小而灵活,以至于你觉得为它使用一个工作的、经过测试的和现成的模块是一种浪费,它应该引入零额外负载,但你更喜欢我们调试您的正则表达式,迫使您可能花费数小时阅读答案和 cmets,并且丝毫不能保证(最多测试一次)结果确实有效?好消息是,遇到这个问题的未来读者可能不会反对在 Perl 中使用模块,并且会很快找到答案。

标签: regex perl html-parsing


【解决方案1】:

我知道你一定听腻了:但你做错了。

我喜欢正则表达式,但它们不是为这类问题而设计的。使用像 Template::Toolkit 这样的标准模板系统会好 1000 倍。

如果您坚持使用这种方法,那么我建议您使用更简单的工具。不要试图让正则表达式强制执行所有规则,而是尽可能使用最基本的正则表达式。在这种情况下,我建议您使用 split 标记您的文本:

use strict;
use warnings;

my $content = do {local $/; <DATA>};

my @tokens = split /(<!--(?:block:.*?|endblock)-->)/, $content;

use Data::Dump;
dd \@tokens;

__DATA__

some html content here top base
<!--block:first-->
    some html content here 1 top
    this <!--comment--> will make it fail here.
    <!--block:second-->
        some html content here 2 top
        <!--block:third-->
            some html content here 3a
            some html content here 3b
        <!--endblock-->
        some html content here 2 bottom
    <!--endblock-->
    some html content here 1 bottom
<!--endblock-->
some html content here bottom base

输出:

[
  "\nsome html content here top base\n",
  "<!--block:first-->",
  "\n    some html content here 1 top\n    this <!--comment--> will make it fail here.\n    ",
  "<!--block:second-->",
  "\n        some html content here 2 top\n        ",
  "<!--block:third-->",
  "\n            some html content here 3a\n            some html content here 3b\n        ",
  "<!--endblock-->",
  "\n        some html content here 2 bottom\n    ",
  "<!--endblock-->",
  "\n    some html content here 1 bottom\n",
  "<!--endblock-->",
  "\nsome html content here bottom base",
]

如您所见,该数组包含文本和您匹配的模式之一之间的交替。

现在,我不知道您的最终目标是什么,也不知道您最终想要什么格式的数据,所以我无法从这里提出任何建议。但是,如果确实满足您的需求,您可以很容易地重新创建原始数据结构。甚至更好的是,您实际上可以执行错误检查,以查找不匹配打开或关闭的块,而您的原始正则表达式会对您隐藏。

附录

我在Perl replace nested blocks regular expression 提供了针对这种方法的扩展完整解决方案

【讨论】:

  • 主要思想是,我想将一些html代码块标记为模板处理的迭代器,同时不破坏可视化html编辑器,所以我现在可以使用这个模板代码&lt;table&gt;&lt;!--block:user--&gt;&lt;tr&gt;&lt;td&gt;$fname&lt;/td&gt;&lt;td&gt;$lname&lt;/td&gt;&lt;/tr&gt;&lt;!--endblock--&gt;&lt;/table&gt;块 'user 是一个迭代器,可以处理并替换为用户名列表。
  • 如果您有重复的嵌套块块,这也不起作用,再次重建数据将非常困难,这个想法很好但不是解决方案。我想我很快就会删除这个问题。
  • 当然,如果您有重复的嵌套块,这将起作用。我在您就该主题发表的原始帖子中创建了full solution。另外,我有点侮辱您想删除此问题,但这甚至是不可能的,因为此答案已被赞成。
  • 你没有受到侮辱,因为解决方案是在第二个原始帖子中提供的,在我在那里发布解决方案之后,我发现它会重复或令人困惑。
【解决方案2】:

尽管建议使用模板或解析器模块,但我保证没有一个模块可以直接处理这个问题。

这是我想出的解决方案。

首先我使用简单的正则表达式在整个内容或模板中找到外部块:

/(<!--block:.*?-->(?:(?:(?!<!--(?:.*?)-->).)|(?R))*?<!--endblock-->)/gis

然后我解析每个外部块以找到其嵌套的子块。

/(<!--block:(.*?)-->((?:(?:(?!<!--block:(?:.*?)-->).)|(?R))*?)<!--endblock-->|((?:(?!<!--.*?-->).)+))/igsx

然后一切正常。我用两个外部块进行了测试,每个块都有嵌套块。

我可以像这样到达任何子块:

print $blocks->{first}->{content};

print $blocks->{first}->{match};

print $blocks->{first}->{second}->{third}->{fourth}->{content}

每个区块哈希引用都有键:

`content`: the block content without the block name and endblock tags.
`match`: the block content with the block name and endblock tags, good for replacing.
`#next`: has the sub block name if exists, good to check if block has children and access them.

下面是经过 Perl 测试的最终代码。

use Data::Dumper;

$/ = undef;
my $content = <DATA>;

my $blocks = parse_blocks($content);

print Dumper($blocks);

#print join "\n", keys( %{$blocks->{first}}); # root blocks names
#print join "\n", keys( %{$blocks->{first}}); # 
#print join "\n", keys( %{$blocks->{first}->{second}});

#print Dumper $blocks->{first};
#print Dumper $blocks->{first}->{content};
#print Dumper $blocks->{first}->{match};

# check if fourth block has sub block.
#print exists $blocks->{first}->{second}->{third}->{fourth}->{'#next'}, "\n";

# check if block has sub block, get it:
#if (exists $blocks->{first}->{second}->{third}->{fourth}->{'#next'}) {
#   print $blocks->{first}->{second}->{third}->{fourth}->{ $blocks->{first}->{second}->{third}->{fourth}->{'#next'} }->{content}, "\n";
#}

exit;
#================================================
sub parse_blocks {
    my ($content) = @_;
    my $href = {};
    # find outer blocks only
    while ($content =~ /(<!--block:.*?-->(?:(?:(?!<!--(?:.*?)-->).)|(?R))*?<!--endblock-->)/gis) {
        # parse each outer block nested blocks
        parse_nest_blocks($href, $1);
    }
    return $href;
}
#================================================
sub parse_nest_blocks {
    my ($aref, $core) = @_;
    my ($k, $v);
    while ( $core =~ /(<!--block:(.*?)-->((?:(?:(?!<!--block:(?:.*?)-->).)|(?R))*?)<!--endblock-->|((?:(?!<!--.*?-->).)+))/igsx )
    {
        if (defined $2) {
           $k = $2; $v = $3;
           $aref->{$k} = {};
           $aref->{$k}->{content} = $v;
           $aref->{$k}->{match} = $1;
           #print "1:{{$k}}\n2:[[$v]]\n";
           my $curraref = $aref->{$k};
           my $ret = parse_nest_blocks($aref->{$k}, $v);
           if ($ret) {
               $curraref->{'#next'} = $ret;
           }
           return $k;
        }
    }

}
#================================================
__DATA__
some html content here top base
<!--block:first-->
    <table border="1" style="color:red;">
    <tr class="lines">
        <td align="left" valign="<--valign-->">
    <b>bold</b><a href="http://www.mewsoft.com">mewsoft</a>
    <!--hello--> <--again--><!--world-->
    some html content here 1 top
    <!--block:second-->
        some html content here 2 top
        <!--block:third-->
            some html content here 3 top
            <!--block:fourth-->
                some html content here 4 top
                <!--block:fifth-->
                    some html content here 5a
                    some html content here 5b
                <!--endblock-->
            <!--endblock-->
            some html content here 3a
            some html content here 3b
        <!--endblock-->
        some html content here 2 bottom
    <!--endblock-->
    some html content here 1 bottom
<!--endblock-->
some html content here1-5 bottom base

some html content here 6-8 top base
<!--block:six-->
    some html content here 6 top
    <!--block:seven-->
        some html content here 7 top
        <!--block:eight-->
            some html content here 8a
            some html content here 8b
        <!--endblock-->
        some html content here 7 bottom
    <!--endblock-->
    some html content here 6 bottom
<!--endblock-->
some html content here 6-8 bottom base

整个哈希转储的输出是:

$VAR1 = {
          'first' => {
                       'second' => {
                                     'third' => {
                                                  'match' => '<!--block:third-->
            some html content here 3 top
            <!--block:fourth-->
                some html content here 4 top
                <!--block:fifth-->
                    some html content here 5a
                    some html content here 5b
                <!--endblock-->
            <!--endblock-->
            some html content here 3a
            some html content here 3b
        <!--endblock-->',
                                                  'content' => '
            some html content here 3 top
            <!--block:fourth-->
                some html content here 4 top
                <!--block:fifth-->
                    some html content here 5a
                    some html content here 5b
                <!--endblock-->
            <!--endblock-->
            some html content here 3a
            some html content here 3b
        ',
                                                  'fourth' => {
                                                                'fifth' => {
                                                                             'match' => '<!--block:fifth-->
                    some html content here 5a
                    some html content here 5b
                <!--endblock-->',
                                                                             'content' => '
                    some html content here 5a
                    some html content here 5b
                '
                                                                           },
                                                                'match' => '<!--block:fourth-->
                some html content here 4 top
                <!--block:fifth-->
                    some html content here 5a
                    some html content here 5b
                <!--endblock-->
            <!--endblock-->',
                                                                'content' => '
                some html content here 4 top
                <!--block:fifth-->
                    some html content here 5a
                    some html content here 5b
                <!--endblock-->
            ',
                                                                '#next' => 'fifth'
                                                              },
                                                  '#next' => 'fourth'
                                                },
                                     'match' => '<!--block:second-->
        some html content here 2 top
        <!--block:third-->
            some html content here 3 top
            <!--block:fourth-->
                some html content here 4 top
                <!--block:fifth-->
                    some html content here 5a
                    some html content here 5b
                <!--endblock-->
            <!--endblock-->
            some html content here 3a
            some html content here 3b
        <!--endblock-->
        some html content here 2 bottom
    <!--endblock-->',
                                     'content' => '
        some html content here 2 top
        <!--block:third-->
            some html content here 3 top
            <!--block:fourth-->
                some html content here 4 top
                <!--block:fifth-->
                    some html content here 5a
                    some html content here 5b
                <!--endblock-->
            <!--endblock-->
            some html content here 3a
            some html content here 3b
        <!--endblock-->
        some html content here 2 bottom
    ',
                                     '#next' => 'third'
                                   },
                       'match' => '<!--block:first-->
    <table border="1" style="color:red;">
    <tr class="lines">
        <td align="left" valign="<--valign-->">
    <b>bold</b><a href="http://www.mewsoft.com">mewsoft</a>
    <!--hello--> <--again--><!--world-->
    some html content here 1 top
    <!--block:second-->
        some html content here 2 top
        <!--block:third-->
            some html content here 3 top
            <!--block:fourth-->
                some html content here 4 top
                <!--block:fifth-->
                    some html content here 5a
                    some html content here 5b
                <!--endblock-->
            <!--endblock-->
            some html content here 3a
            some html content here 3b
        <!--endblock-->
        some html content here 2 bottom
    <!--endblock-->
    some html content here 1 bottom
<!--endblock-->',
                       'content' => '
    <table border="1" style="color:red;">
    <tr class="lines">
        <td align="left" valign="<--valign-->">
    <b>bold</b><a href="http://www.mewsoft.com">mewsoft</a>
    <!--hello--> <--again--><!--world-->
    some html content here 1 top
    <!--block:second-->
        some html content here 2 top
        <!--block:third-->
            some html content here 3 top
            <!--block:fourth-->
                some html content here 4 top
                <!--block:fifth-->
                    some html content here 5a
                    some html content here 5b
                <!--endblock-->
            <!--endblock-->
            some html content here 3a
            some html content here 3b
        <!--endblock-->
        some html content here 2 bottom
    <!--endblock-->
    some html content here 1 bottom
',
                       '#next' => 'second'
                     },
          'six' => {
                     'match' => '<!--block:six-->
    some html content here 6 top
    <!--block:seven-->
        some html content here 7 top
        <!--block:eight-->
            some html content here 8a
            some html content here 8b
        <!--endblock-->
        some html content here 7 bottom
    <!--endblock-->
    some html content here 6 bottom
<!--endblock-->',
                     'content' => '
    some html content here 6 top
    <!--block:seven-->
        some html content here 7 top
        <!--block:eight-->
            some html content here 8a
            some html content here 8b
        <!--endblock-->
        some html content here 7 bottom
    <!--endblock-->
    some html content here 6 bottom
',
                     'seven' => {
                                  'match' => '<!--block:seven-->
        some html content here 7 top
        <!--block:eight-->
            some html content here 8a
            some html content here 8b
        <!--endblock-->
        some html content here 7 bottom
    <!--endblock-->',
                                  'content' => '
        some html content here 7 top
        <!--block:eight-->
            some html content here 8a
            some html content here 8b
        <!--endblock-->
        some html content here 7 bottom
    ',
                                  'eight' => {
                                               'match' => '<!--block:eight-->
            some html content here 8a
            some html content here 8b
        <!--endblock-->',
                                               'content' => '
            some html content here 8a
            some html content here 8b
        '
                                             },
                                  '#next' => 'eight'
                                },
                     '#next' => 'seven'
                   }
        };

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    • 2011-10-29
    • 2010-11-14
    • 2012-12-31
    • 1970-01-01
    相关资源
    最近更新 更多