2010-05-05 125 views
2

我有Perl代码:如何用Perl执行多个替换?

my $s = "The+quick+brown+fox+jumps+over+the+lazy+dog+that+is+my+dog"; 

我想替换每个+空间和dogcat

我有这样的正则表达式:

$s =~ s/\+(.*)dog/ ${1}cat/g; 

但是,这只是+和最后dog第一次出现的匹配。

+5

这难道不是简化的东西,为此使用两个单独的正则表达式替换吗? – WhirlWind 2010-05-05 00:42:15

+0

请问真正的问题? – codeholic 2010-05-05 05:25:34

+1

如果你正在寻找性能,你应该问你的问题,但获得性能的方式可能是完全没有正则表达式。例如,你试过tr吗? – WhirlWind 2010-05-05 20:09:31

回答

6

可以使用“E”改性剂在s///表达式的第二部分来执行代码。

$s =~ s/(\+)|(dog)/$1 ? ' ' : 'cat'/eg; 

如果$1为真,则表示匹配\+,所以它替换的空间;否则它会替代“猫”。

+0

为什么打扰捕捉狗? – 2010-05-05 18:51:27

+0

这个解决方案效果很好,但是当我运行配置文件时,似乎两条线比一条线工作得更快,因为它必须调用CORE:substcont而不仅仅是CORE:subst。但无论如何非常感谢你 我已经有两行解决方案。发生什么事是我有一个文件与100K行的字符串,我将需要在插入到数据库之前进行标准化。我正在努力加快速度。如果我跑过正则表达式比赛的话,我会感到印象不够 – user332951 2010-05-05 19:22:55

+2

乍得:为了防止它在邻近区域内散乱跑? :)没错,不需要捕捉“狗”。 '$ s =〜s /(\ +)| dog/$ 1? '':'cat'/ eg;' – Brock 2010-05-05 22:49:29

4

答案很简单 - 使用2线!:

$s =~ s/+/ /g; 
$s =~ s/dog/cat/g; 

它很可能在一个行完成与“非贪婪”匹配,但是这应该做的伎俩

8

两个正则表达式可能使你的生活轻松了许多:

$s =~ s/\+/ /g; 
$s =~ s/dog/cat/g; 

下面的比赛“+”,后面一堆东西,其次是“狗”。另外,“+”在技术上是一个元字符。

/+(.*)dog/ 
+0

我已经有两行解决方案。发生什么事是我有一个文件与100K行的字符串,我将需要在插入到数据库之前进行标准化。我正在努力加快速度。如果我跑过正则表达式匹配,我会感到印象不够,如果我必须这样做两次 – user332951 2010-05-05 18:07:51

+0

考虑到下面的新答案之一,这似乎是错误的印象。 – 2010-05-05 18:37:40

+0

@Mark谢谢;我评论了这个问题。 – WhirlWind 2010-05-05 20:11:10

4

散列可以做你想做的:

#!/usr/bin/perl 

use strict; 
use warnings; 

my $s = "The+quick+brown+fox+jumps+over+the+lazy+dog+that+is+my+dog"; 

my %replace = (
    "+" => " ", 
    dog => "cat", 
); 

$s =~ s/([+]|dog)/$replace{$1}/g; 

print "$s\n"; 

在我看到你所关心的性能的意见,这两个正则表达式的解决方案是更好的性能。这是因为任何适用于一个正则表达式的解决方案都需要使用捕获(这会减慢正则表达式)。

这里有一个基准的结果:

eval: The quick brown fox jumps over the lazy cat that is my cat 
hash: The quick brown fox jumps over the lazy cat that is my cat 
two: The quick brown fox jumps over the lazy cat that is my cat 
     Rate hash eval two 
hash 33184/s -- -29% -80% 
eval 46419/s 40% -- -72% 
two 165414/s 398% 256% -- 

我用下面的基准:

#!/usr/bin/perl 

use strict; 
use warnings; 

use Benchmark; 

my $s = "The+quick+brown+fox+jumps+over+the+lazy+dog+that+is+my+dog"; 

my %replace = (
    "+" => " ", 
    dog => "cat", 
); 

my %subs = (
    hash => sub { 
     (my $t = $s) =~ s/([+]|dog)/$replace{$1}/g; 
     return $t; 
    }, 
    two => sub { 
     (my $t = $s) =~ s/[+]/ /g; 
     $t =~ s/dog/cat/g; 
     return $t; 
    }, 
    eval => sub { 
     (my $t = $s) =~ s/(\+)|(dog)/$1 ? ' ' : 'cat'/eg; 
     return $t; 
    }, 
); 

for my $k (sort keys %subs) { 
    print "$k: ", $subs{$k}(), "\n"; 
} 

Benchmark::cmpthese -1, \%subs; 
1

如果速度是重要的,你应该有两行坚持。但是,当我需要立即执行多个替换时,我通常更关心便利性,所以我使用Chas所建议的散列。欧文斯。与双线相比,两个优点是易于修改,并且其行为与预期相同(例如,当“猫”替代“猫”时,“猫”替代)。

然而,我感到非常懒手工编写的正则表达式,喜欢组装它与加盟,并使用地图逃避的东西:

#!/usr/bin/perl 

use strict; 
use warnings; 

my $s = "The+quick+brown+fox+jumps+over+the+lazy+dog+that+is+my+dog"; 

my %replace = (
    "+" => " ", 
    dog => "cat", 
); 

my $regex = join "|", 
    #use quotemeta to escape special characters 
    map { quotemeta } 
    #reverse sort the keys because "ab" =~ /(a|ab)/ returns "a" 
    sort { $b cmp $a } keys %replace; 

#compiling the regex before using it prevents 
#you from having to recompile it each time 
$regex = qr/$regex/; 

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

print "$s\n"; 
+0

我自己更喜欢代码重复性和方便性,但是对于这样的特殊情况,我必须读取字符串,每隔3分钟从100k到1Mils的范围内读取字符串。因此,我不得不担心速度或事情会在队列中堵塞 – user332951 2010-05-05 21:02:04

+0

Chas:感谢您的更正。我只用了几天的计算器,我已经在学习有用的东西了。我应该去寻找我可能使用过这种代码的地方......只有一个问题,为什么不是s/$ regex/$ replace {$&}/g? – Pontus 2010-05-08 15:06:15

+0

duenguyen:听起来很有趣!来自perldoc(http://perldoc.perl.org/perlvar.html#%24%26)的 – Pontus 2010-05-08 15:07:45

3

Perl的5.14和更新与能力链换人一个非破坏性的任务,所以你可以用一块石头杀死3只鸟:做你的两个全局替换,并将结果赋给一个新的变量而不修改你的原始变量。

my $s = "The+quick+brown+fox+jumps+over+the+lazy+dog+that+is+my+dog"; 
my $result = $s =~ s/+/ /gr 
       =~ s/dog/cat/gr; 

将取代所有的+空间和与cat替换每dog,将结果分配到一个新的变量。在一个班轮。