Perl正则表达式/替换嵌套短语

时间:2012-08-13 00:12:19

标签: regex perl

我有一个perl脚本,逐行处理文本文件并将这些行中的短语转换为链接(特别是在mediawiki标记中,但我怀疑任何标记都会有同样的问题)。我被困的地方是一个短语是另一个短语的子集。在这些情况下,会创建太多链接。

例如,如果“总务委员会”和“年度总务委员会会议”是两个短语:

  

总务委员会会议每月举行一次会议。

正确转换为:

  

[[#GC |总务委员会]]会议每月举行一次。

然而,

  

年度总务委员会会议将于5月举行。

错误地转换为:

  <[#AGCM |年度[[#GC |总务委员会]]会议]]将于5月举行。

也就是说,我的剧本在“年度总务委员会会议”中找到“总务委员会”这一短语,并插入我不想要的链接。在这个例子中应该只有一个指向AGCM的链接。

相关的perl代码是:

my($line) = $_;
foreach $phrase (keys(%phrases))  # the phrases to replace mapped to their links
{
    my($link) = $phrases{$phrase};
    if ($line =~ m/$phrase/)
    {
        $line =~ s/$phrase/[[#$link|$phrase]]/g;
    }
}

当一个短语可以与另一个短语找到时,如何避免匹配/替换的任何建议?

更新:根据一些问题进行澄清:每个短语都是独立的;没有一个优先于另一个。在最短的时间内取得最长的时间足以得到我所需要的东西。

1 个答案:

答案 0 :(得分:4)

您应该构建一个与一个比较中的任何哈希键匹配的正则表达式。

这个程序显示了这个想法。通过减少长度来排序键,以便首先找到最长匹配,然后将|交替字符连接为分隔符。

然后,只需查找所有出现的构建模式并将其替换为相应的哈希元素值即可。这可以在一次替换中完成,而不需要循环。

请注意,您可能需要考虑插入map以使用\s+代替空格,并且可能在字符串之前和之后放置\b以确保匹配的字符串isn' t一个较长的单词的一部分。此外,/i正则表达式修饰符可能与允许与大小写无关的匹配相关。

use strict;
use warnings;

my %phrases = (
  'General Committee' => '[[#GC|General Committee]]',
  'Annual General Committee Meeting' => '[[#AGCM|Annual General Committee Meeting]]',
);

my $text = <<END;
The General Committee meeting shall meet once a month.
The Annual General Committee Meeting shall be held in May.
END

my $regex = join '|', sort { length $b <=> length $a } keys %phrases;

$text =~ s/($regex)/$phrases{$1}/g;

print $text, "\n";

<强>输出

The [[#GC|General Committee]] meeting shall meet once a month.
The [[#AGCM|Annual General Committee Meeting]] shall be held in May.