Как выполнить несколько замен с помощью Perl? - PullRequest
2 голосов
/ 05 мая 2010

У меня есть код Perl:

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

Я хочу заменить каждые + пробелом и dog на cat.

У меня есть это регулярное выражение:

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

Но это соответствует только первому вхождению + и последнему dog.

Ответы [ 7 ]

8 голосов
/ 05 мая 2010

Два регулярных выражения могут сделать вашу жизнь намного проще:

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

Следующие совпадения: «+», затем куча вещей, затем «собака». Кроме того, «+» технически является метасимволом.

/+(.*)dog/
6 голосов
/ 05 мая 2010

Вы можете использовать модификатор 'e' для выполнения кода во второй части выражения s///.

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

Если $1 истинно, это означает, что \+ соответствует, поэтому он заменяет пробел; в противном случае он заменяет «кошка».

4 голосов
/ 24 марта 2017

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; 

Заменит все ваши + пробелом и заменит каждый dog на cat, присвоив результат новой переменной. В однострочнике.

4 голосов
/ 05 мая 2010

Хеш может делать то, что вы хотите:

#!/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;
4 голосов
/ 05 мая 2010

Простой ответ - используйте 2 строки!:

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

Возможно, это можно сделать в одну строку с «не жадным» соответствием, но это должно сработать

1 голос
/ 06 мая 2010

Если важна скорость, вам следует придерживаться двух строк. Но когда мне нужно сделать несколько подстановок одновременно, я обычно больше беспокоюсь об удобстве, поэтому я использую хеш, как предложено Chas. Оуэнс. Два преимущества по сравнению с двухсторонним заключением заключаются в том, что его легко модифицировать, и он ведет себя как ожидалось (например, при одновременной замене «cat» на «dog» и «dog» на «cat»).

Тем не менее, я очень ленив, чтобы написать регулярное выражение вручную, и предпочитаю собирать его с помощью соединения и использовать карту для экранирования:

#!/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 голосов
/ 17 мая 2018

Я знаю, что это старая ветка, но вот одна строчка для Perls до v5.14:

my $s = 'The+quick+brown+fox+jumps+over+the+lazy+dog+that+is+my+dog';
$s = do {local $_ = $s; s/\+/ /g; s/dog/cat/g; $_};
...