改善perl平衡正则表达式

时间:2017-08-12 08:19:32

标签: regex perl

我使用以下perl正则表达式从输入中清除xml / html样式格式化标记。

$expr = qr{
    <\s*a(?:\s*|\s+[^>]+)>
    ((?:
        (?> (?:(?!(<\s*a(?:\s*|\s+[^>]+)>|<\/\s*a\s*>)).)+ )
      |
        (??{ $expr })
    )*)
    <\/\s*a\s*>
  }x;

递归地应用它会删除嵌套的<a>...</a>标记(如果<a>生成超链接,这不会有意义)并且只保留括号内的文本:

    my $tmp_text = "a<a> e </a>c<a href="test">g <a> d</a> d</a>f";
    print $tmp_text."\n";

    $tmp_text=~s/$expr/$1/g;
    print $tmp_text."\n";

    $tmp_text=~s/$expr/$1/g;
    print $tmp_text."\n";

这将打印

    a<a> e </a>c<a href="test">g <a> d</a> d</a>f
    a e cg <a> d</a> df
    a e cg  d df

现在,我想对所有其他格式标记执行相同操作,例如<b>..</b>等。我肯定会列出所有支持的代码,在a中将b替换为$expr等,然后对每个代码重复替换。

但是,我想知道是否有更高效/更紧凑的方式修改$expr,以便它会对name中的<name something>...</name>进行平衡匹配。

请注意,我有意识地避免使用perl包进行xml / html解析或清理工具。我正在处理的输入不是严格的html,我不想包含依赖项。

1 个答案:

答案 0 :(得分:2)

我相信这符合您的要求:

我更换了&#39; a&#39;在正则表达式中使用[a-z] +,捕获并反向引用它。这意味着您必须更改应用它的行代替$2

如果您想要列出已接受的标签(这对我来说似乎更好,但我不知道您的用例),您可以将[a-z]+替换为例如可接受标签列表由|加入。

$expr = qr{
    <\s*([a-z]+)(?:\s*|\s+[^>]+)>
    ((?:
        (?> (?:(?!(<\s*\1(?:\s*|\s+[^>]+)>|<\/\s*\1\s*>)).)+ )
      |
        (??{ $expr })
    )*)
    <\/\s*\1\s*>
  }x;

带有标记的简短示例脚本:

#!/usr/bin/env perl

use strict;
use warnings;

my $expr;

$expr = qr{
    <\s*([a-z]+)(?:\s*|\s+[^>]+)>
    ((?:
        (?> (?:(?!(<\s*\1(?:\s*|\s+[^>]+)>|<\/\s*\1\s*>)).)+ )
      |
        (??{ $expr })
    )*)
    <\/\s*\1\s*>
  }x;


my $tmp_text = 'a<b> e </b>c<b href="test">g <b> d</b> d</b>f';
print $tmp_text."\n";

print $tmp_text."\n" while $tmp_text =~s/$expr/$2/g;

Wiktor在评论中发布了一个正则表达式,也允许使用大写字母和&#39; _&#39; - 如果这是您想要的,只需将[a-z]替换为[a-zA-Z_],就像他的例子一样。