2010-05-05 3 views
2

J'ai code Perl:Comment effectuer plusieurs remplacements avec Perl?

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

Je veux remplacer tous les + avec l'espace et dog avec cat.

J'ai cette expression régulière:

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

Mais, il correspond uniquement à la première occurrence de + et dernier dog.

+5

Cela ne simplifierait-il pas l'utilisation de deux substitutions d'expressions régulières distinctes? – WhirlWind

+0

Voulez-vous s'il vous plaît poser la vraie question? – codeholic

+1

Si vous recherchez des performances, vous devriez avoir demandé dans votre question, mais la façon d'obtenir des performances pourrait être de le faire entièrement sans expressions régulières. Avez-vous essayé tr, par exemple? – WhirlWind

Répondre

6

Vous pouvez utiliser le modificateur « e » pour exécuter du code dans la deuxième partie d'une expression s///.

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

Si $1 est vrai, cela signifie que le \+ adapté, il substitue un espace; sinon, il remplace "chat".

+0

Pourquoi s'embêter à capturer un chien? –

+0

cette solution fonctionne très bien, mais quand j'ai couru à travers le profil, il semble que deux lignes travaillent plus vite qu'une ligne, car il doit appeler CORE: substcont plutôt juste CORE: subst. Mais de toute façon merci beaucoup J'ai déjà deux lignes de solution en place. Qu'est-ce qui se passe est que j'ai un fichier avec une chaîne de 100k de lignes dont j'ai besoin pour le normaliser avant d'insérer dans DB. J'essaie d'accélérer les choses. J'étais sous l'impression si j'ai couru à travers regexp match une fois sera plus rapide si je dois le faire deux fois – user332951

+2

Tchad: Pour l'empêcher de courir autour du quartier? :) Juste, pas besoin de capturer "chien". '$ s = ~ s/(\ +) | chien/$ 1? '': 'cat'/eg; ' – Brock

4

réponse simple - utiliser 2 lignes !:

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

Il pourrait probablement se faire en une ligne avec correspondance « non gourmand », mais cela devrait faire l'affaire

8

Deux expressions régulières pourraient faire votre vie beaucoup plus facile:

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

Les matches suivants « + », suivi d'un tas de choses, suivi de « chien ». En outre, "+" est techniquement un métacaractère.

/+(.*)dog/ 
+0

J'ai déjà deux lignes de solution en place. Qu'est-ce qui se passe est que j'ai un fichier avec une chaîne de 100k de lignes dont j'ai besoin pour le normaliser avant d'insérer dans DB. J'essaie d'accélérer les choses. J'étais sous l'impression si j'ai couru à travers regexp match une fois sera plus rapide si je dois le faire deux fois – user332951

+0

Cela semble être une mauvaise impression étant donné l'une des réponses les plus récentes ci-dessous. –

+0

@Marque merci; J'ai commenté la question. – WhirlWind

4

Un hachage peut faire ce que vous voulez:

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

Dans les commentaires, je vois que vous êtes préoccupé par la performance, la solution à deux regex est plus performante. C'est parce que toute solution qui fonctionne pour une regex devra utiliser des captures (qui ralentissent l'expression rationnelle).

Voici les résultats d'une référence:

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% -- 

J'ai utilisé la référence suivante:

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

Si la vitesse est importante, vous devriez probablement en tenir à deux lignes. Mais quand j'ai besoin de faire plusieurs sous-groupes à la fois, je m'intéresse généralement plus à la commodité, alors j'utilise un hash comme suggéré par Chas. Owens. Deux avantages par rapport au doubleur étant qu'il est facile de le modifier, et il se comporte comme prévu (par exemple en substituant "chat" pour "chien" et "chien" pour "chat" en même temps).

Cependant, je suis beaucoup trop paresseux pour écrire l'expression rationnelle à la main et préfère assembler par join et utiliser la carte pour échapper à des choses:

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

Moi-même préfèrent la capacité de code et la commodité à tout moment, mais pour ce cas particulier que je fais, je dois lire une chaîne de 100k à 1Mils de ligne toutes les 3 minutes. Par conséquent, je dois me soucier de la vitesse ou les choses vont se boucher dans la file d'attente – user332951

+0

Chas: Merci pour les corrections. Je n'utilise que stackoverflow depuis quelques jours et j'apprends déjà des choses utiles. Je devrais probablement aller chercher des endroits où j'ai peut-être utilisé ce genre de code ... Juste une question, pourquoi pas s/$ regex/$ replace {$ &}/g? – Pontus

+0

duenguyen: ça a l'air amusant! – Pontus

3

Perl 5.14 et plus récent a la capacité de substitutions de chaîne avec une affectation non-destructrice de sorte que vous pouvez tuer 3 oiseaux d'une pierre: faites vos deux substitutions globales et assignez le résultat à une nouvelle variable sans modifier votre variable d'origine.

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

remplacera tous vos + avec l'espace et remplacer tous les dog avec cat, attribuer le résultat dans une nouvelle variable. Dans un one-liner.

Questions connexes