如何在同一个字符串上有效地处理多个Perl搜索/替换操作?

Jef*_*eff 5 regex perl hash substitution

所以我的Perl脚本基本上是一个字符串然后尝试通过多次搜索并替换它来清理它,如下所示:

$text =~ s/<[^>]+>/ /g;
$text =~ s/\s+/ /g;
$text =~ s/[\(\{\[]\d+[\(\{\[]/ /g;
$text =~ s/\s+[<>]+\s+/\. /g;
$text =~ s/\s+/ /g;
$text =~ s/\.*\s*[\*|\#]+\s*([A-Z\"])/\. $1/g; # replace . **** Begin or . #### Begin or ) *The 
$text =~ s/\.\s*\([^\)]*\) ([A-Z])/\. $1/g; # . (blah blah) S... => . S...
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我正在处理令人讨厌的HTML,并且必须将其击败.

我希望有一种更简单,美观的方式来做到这一点.我有大约50行看起来就像上面那样.

我通过使用哈希来解决了这个问题的一个版本,其中键是注释,哈希是reg表达式,如下所示:

%rxcheck = (
        'time of day'=>'\d+:\d+', 
    'starts with capital letters then a capital word'=>'^([A-Z]+\s)+[A-Z][a-z]',
    'ends with a single capital letter'=>'\b[A-Z]\.'
}
Run Code Online (Sandbox Code Playgroud)

这就是我使用它的方式:

 foreach my $key (keys %rxcheck) {
if($snippet =~ /$rxcheck{ $key }/g){ blah blah  }
 }
Run Code Online (Sandbox Code Playgroud)

当我尝试使用哈希表示密钥是表达式并且它指向我想要替换它的那个问题时,问题就出现了......并且它有一个1美元或2美元.

%rxcheck2 = (
        '(\w) \"'=>'$1\"'
}
Run Code Online (Sandbox Code Playgroud)

以上是这样做的:

$snippet =~ s/(\w) \"/$1\"/g;
Run Code Online (Sandbox Code Playgroud)

但我似乎无法将"$ 1"部分传递到正则表达式字面上(我认为这是正确的单词......即使我使用'标记,似乎正在解释$ 1.)因此,这导致:

if($snippet =~ /$key/$rxcheck2{ $key }/g){  }
Run Code Online (Sandbox Code Playgroud)

这不起作用.

那2个问题:

简单:如何以一种易于编辑的方式处理大量的正则表达式,这样我就可以更改和添加它们而不必仅仅剪切和粘贴线条?

更难:如何处理它们使用哈希(或数组,如果我有,例如,我想要包括多个部分,如1)部分搜索,2)替换3)评论,4)全局/不区分大小写修饰符),如果这实际上是最简单的方法吗?

谢谢你的帮助 -

j_r*_*ker 10

问题#1

由于单个正则表达式似乎没有多少共享结构,因此实际上并没有比仅仅列出命令更简单或更清晰的方式.减少像这样的代码重复的一种常见方法是$text进入$_,这样就不必说:

$text =~ s/foo/bar/g;
Run Code Online (Sandbox Code Playgroud)

你可以说:

s/foo/bar/g;
Run Code Online (Sandbox Code Playgroud)

这样做的一个常见习惯是使用简并for()循环作为局部化器:

for($text)
{
  s/foo/bar/g;
  s/qux/meh/g;
  ...
}
Run Code Online (Sandbox Code Playgroud)

该模块的范围将保留的任何预先存在的价值$_,因此没有必要明确localIZE $_.

在这一点上,你已经消除了几乎所有非样板特征 - 即使在理论上它也能缩短多少?

除非你真正想要的(正如你的问题#2所暗示的)是改进的模块化,例如,迭代,报告,计数等所有正则表达式的能力.

问题#2

您可以使用qr//语法引用替换的"搜索"部分:

my $search = qr/(<[^>]+>)/;
$str =~ s/$search/foo,$1,bar/;
Run Code Online (Sandbox Code Playgroud)

但是,我不知道如何充分引用"替换"部分.我原本希望这qr//也适用于此,但事实并非如此.有两种选择值得考虑:

1. eval()foreach循环中使用. 这将使您能够保留当前的%rxcheck2哈希值.缺点:你应该始终关注字符串eval()的安全性.

2.使用匿名子例程数组:

my @replacements = (
    sub { $_[0] =~ s/<[^>]+>/ /g; },
    sub { $_[0] =~ s/\s+/ /g; },
    sub { $_[0] =~ s/[\(\{\[]\d+[\(\{\[]/ /g; },
    sub { $_[0] =~ s/\s+[<>]+\s+/\. /g },
    sub { $_[0] =~ s/\s+/ /g; },
    sub { $_[0] =~ s/\.*\s*[\*|\#]+\s*([A-Z\"])/\. $1/g; },
    sub { $_[0] =~ s/\.\s*\([^\)]*\) ([A-Z])/\. $1/g; }
);

# Assume your data is in $_
foreach my $repl (@replacements) {
    &{$repl}($_);
}
Run Code Online (Sandbox Code Playgroud)

您当然可以使用哈希,而使用一些更有用的键作为哈希,和/或您可以使用包含注释或其他信息的多值元素(或哈希值).

  • / e只是一个字符串eval./ ee是一回事,但你取第一个/ e的结果再做一次.通过添加或减去/ e没有安全功能. (3认同)