初学者正则表达式:多个替代

时间:2009-09-25 09:15:23

标签: regex perl replace

我有一个字符串:

$mystring = "My cat likes to eat tomatoes.";

我想用regex对这个字符串进行两次替换。我想s/cat/dog/s/tomatoes/pasta/。但是,我不知道如何正确格式化正则表达式,在一个表达式中,在一个声明中,在一个表达式中执行多个替换。现在,我只有:

$mystring =~ s/cat/dog/ig;
$mystring =~ s/tomatoes/pasta/ig;

6 个答案:

答案 0 :(得分:15)

像往常一样,使用哈希作为查找表,匹配键,替换为值:

#!/usr/bin/perl

use strict;
use warnings;

use Regex::PreSuf;

my %repl = (
    cat => 'dog',
    tomatoes => 'pasta',
);

my $string = "My cat likes to eat tomatoes.";
my $re = presuf( keys %repl );

$string =~ s/($re)/$repl{$1}/ig;

print $string, "\n";

输出:

C:\Temp> t
My dog likes to eat pasta.

你也可以使用循环:

for my $k ( keys %repl ) {
    $string =~ s/\Q$k/$repl{$k}/ig;
}

答案 1 :(得分:6)

你为什么要这样?

我知道一些Perl-ers为能够编写一些可以想象的最混淆的代码而感到自豪(参见这里的一些代码高尔夫问题),但这并不是一件明智的事情。< / p>

保持可读性,并保持这样,从长远来看,你会感谢自己。

编辑:

当然,如果你正在寻找5个或更多的替代品,请(对于上帝的母亲)使用某种查找表。但是,不要试图编写一个能够完成所有工作的大规模正则表达式。

答案 2 :(得分:3)

如果您正在寻找的东西本身就是正则表达式,那么perl@SinanÜnür的直接查找表将不起作用(因为字符串相等123 eq '\d+'失败)。

您可以使用Regexp::Assemble来解决此限制:

use strict;
use warnings;
use Regexp::Assemble;

my %replace = (
    'cat' => 'dog',
    '(?:tom|pot)atoes' => 'pasta',
);
my $re = Regexp::Assemble->new->track(1)->add(keys %replace);

my $str = 'My cat likes to eat tomatoes.';
while (my $m = $re->match($str)) {
    $str =~ s/$m/$replace{$m}/;
}
print $str, $/;

$str = 'My cat likes to eat potatoes.';
while (my $m = $re->match($str)) {
    $str =~ s/$m/$replace{$m}/;
}
print $str, $/;

这两个块都产生My dog likes to eat pasta.

答案 3 :(得分:2)

我建议你这样做

my $text               =  'My cat likes to eat tomatoes.';
my ( $format = $text ) =~ s/\b(cat|tomatoes)\b/%s/g;

然后你可以这样做:

my $new_sentence = sprintf( $format, 'dog', 'pasta' ); 

以及:

$new_sentence    = sprintf( $format, 'tiger', 'asparagus' );

我和其他人一起去。你不应该想要在一个表达式或一行中完成所有操作......但这是一种方式:

$text =~ s/\b(cat|tomatoes)\b/ ${{ qw<cat dog tomatoes pasta> }}{$1} /ge;

答案 4 :(得分:1)

在一行中执行多个替换的一种非常基本方法是将文本与分组匹配。这将不允许您找到所有“猫”的实例并将其替换为“狗”,但它会让您“我的狗喜欢吃意大利面”

$mystring =~ s/(.*)cat(.*)tomatoes(.*)/$1dog$2pasta$3/g;

答案 5 :(得分:1)

你可以快速而肮脏的方式,或者快速而干净的方式:

在这两种情况下,您都需要哈希word => replacement

使用快速而肮脏的方式,然后通过使用“|”连接哈希的键来构建替换的左侧部分。为了处理重叠的单词(例如'cat'和'catogan'),你需要先放置最长的选项,方法是在哈希的键上做sort reverse。你仍然无法处理要替换的单词中的元字符(例如'cat ++')。

快速而干净的方式使用Regexp::Assemble构建正则表达式的左侧部分。它本身处理重叠的单词,并且很容易让它处理要替换的单词中的元字符。

一旦有了要替换的单词,就可以用散列中的相应条目替换它。

下面是一些显示2种方法的代码,处理各种情况:

#!/usr/bin/perl

use strict;
use warnings;

use Test::More tests => 6;

use Regexp::Assemble;

my $mystring = "My cat likes to eat tomatoes.";
my $expected = "My dog likes to eat pasta.";

my $repl;

# simple case
$repl= { 'cat' => 'dog', 'tomatoes' => 'pasta', };

is( 
    repl_simple($mystring, $repl), 
    $expected, 
    'look Ma, no module (simple)'
);  

my $re= regexp_assemble($repl);
is( 
    repl_assemble($mystring, $re), 
    $expected, 
    'with Regex::Assemble (simple)'
);

# words overlap
$mystring = "My cat (catogan) likes to eat tomatoes.";
$expected = "My dog (doggie) likes to eat pasta.";

$repl= {'cat' => 'dog', 'tomatoes' => 'pasta', 'catogan'  => 'doggie', };

is( 
    repl_simple($mystring, $repl), 
    $expected, 
    'no module, words overlap'
);  

$re= regexp_assemble( $repl);
is( 
     repl_assemble($mystring, $re), 
     $expected, 
     'with Regex::Assemble, words overlap'
);


# words to replace include meta-characters
$mystring = "My cat (felines++) likes to eat tomatoes.";
$expected = "My dog (wolves--) likes to eat pasta.";

$repl= {'cat' => 'dog', 'tomatoes' => 'pasta', 'felines++' => 'wolves--', };

is( 
    repl_simple($mystring, $repl), 
    $expected, 
    'no module, meta-characters in expression'
);  

$re= regexp_assemble( $repl);
is( 
    repl_assemble($mystring, $re), 
    $expected, 
    'with Regex::Assemble, meta-characters in expression'
);

sub repl_simple { 
    my( $string, $repl)= @_;
    my $alternative= join( '|', reverse sort keys %$repl);
    $string=~ s{($alternative)}{$repl->{$1}}ig;
    return $string;
  }


sub regexp_assemble { 
    my( $repl)= @_;
    my $ra = Regexp::Assemble->new;
    foreach my $alt (keys %$repl)
      { $ra->add( '\Q' . $alt . '\E'); }
    return $ra->re;
  } 

sub repl_assemble { 
    my( $string, $re)= @_;
    $string=~ s{($re)}{$repl->{$1}}ig;
    return $string;
  }