2010-05-05 4 views
2

У меня есть код Perl:Как выполнить множественные замены с помощью 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.

+5

Не упростит ли это использование двух отдельных замещений регулярных выражений для этого? – WhirlWind

+0

Не могли бы вы задать реальный вопрос? – codeholic

+1

Если вы ищете производительность, вы должны спросить в своем вопросе, но способ получить производительность может состоять в том, чтобы сделать это полностью без регулярных выражений. Например, вы пытались tr? – WhirlWind

ответ

6

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

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

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

+0

Зачем беспокоиться о захвате собаки? –

+0

это решение отлично работает, но когда я просматривал профиль, кажется, что две линии работают быстрее, чем одна строка, потому что он должен вызывать CORE: substcont, а не CORE: subst. Но в любом случае большое спасибо У меня уже есть решение на две линии. Случилось так, что у меня есть файл со строкой из 100 тыс. Строк, которые мне нужно будет нормализовать, прежде чем вставлять в БД. Я пытаюсь ускорить процесс. Я был под впечатлением, если я пропустил регулярное совпадение, одно время будет быстрее, если я должен сделать это два раза. – user332951

+2

Чад: Чтобы он не бегал по окрестностям? :) Правильно, нет необходимости захватывать «собаку». '$ s = ~ s/(\ +) | dog/$ 1? '': 'cat'/eg; ' – Brock

4

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

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

Это, вероятно, может быть сделано в одной строке с «не жадный» соответствия, но это должно сделать трюк

8

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

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

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

/+(.*)dog/ 
+0

У меня уже есть решение на две линии. Случилось так, что у меня есть файл со строкой из 100 тыс. Строк, которые мне нужно будет нормализовать, прежде чем вставлять в БД. Я пытаюсь ускорить процесс. Я был под впечатлением, если я пропустил регулярное совпадение, одно время будет быстрее, если я должен сделать это два раза. – user332951

+0

Это похоже на неправильное впечатление, учитывая один из более новых ответов ниже. –

+0

@Mark thanks; Я прокомментировал этот вопрос. – WhirlWind

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

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

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

#!/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

Я сам предпочитаю возможность повторного использования кода и удобство в любое время, но для этого особого случая, который я делаю, мне нужно читать строку, которая от 100 к до 1 миллиметра линии каждые 3 минуты. Поэтому я должен беспокоиться о скорости, или все будет забито в очереди – user332951

+0

Chas: Спасибо за исправления. Я использовал только stackoverflow в течение нескольких дней, и я уже изучаю полезные вещи. Я, вероятно, должен искать места, где я, возможно, использовал этот тип кода ... Только один вопрос, почему бы не s/$ regex/$ replace {$ &}/g? – Pontus

+0

duenguyen: звучит как весело! – Pontus

3

Perl 5,14 и выше имеет способность цепных подстановок с неразрушающего назначения, чтобы вы могли убить трех птиц одним камнем: выполните две ваши глобальные замены и назначьте результат новой переменной без изменения исходной переменной.

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, назначая результат в новую переменную. В однострочном.

Смежные вопросы