Comment effectuer une substitution Perl sur une chaîne tout en conservant l'original?

180

En Perl, quel est le bon moyen d'effectuer un remplacement sur une chaîne à l'aide d'une expression régulière et de stocker la valeur dans une variable différente, sans changer l'original?

Habituellement, je copie simplement la chaîne dans une nouvelle variable, puis la lie à l' s///expression régulière qui effectue le remplacement sur la nouvelle chaîne, mais je me demandais s'il existe une meilleure façon de le faire?

$newstring = $oldstring;
$newstring =~ s/foo/bar/g;
Kaybenleroll
la source

Réponses:

257

C'est l'idiome que j'ai toujours utilisé pour obtenir une copie modifiée d'une chaîne sans changer l'original:

(my $newstring = $oldstring) =~ s/foo/bar/g;

Dans perl 5.14.0 ou version ultérieure, vous pouvez utiliser le nouveau /r modificateur de substitution non destructive :

my $newstring = $oldstring =~ s/foo/bar/gr; 

Remarque: les solutions ci-dessus fonctionnent sans gtrop. Ils fonctionnent également avec tous les autres modificateurs.

John Siracusa
la source
6
Que ce soit ou non sous utilisation stricte.
Portée
Je me demandais si quelque chose comme ça my $new = $_ for $old =~ s/foo/bar;fonctionnerait?
Benoit
2
@Benoit, je crois que vous voulez dire que s/foo/bar/ for my $newstring = $oldstring;cela fonctionne, mais c'est beaucoup plus étrange.
ikegami
43

La déclaration:

(my $newstring = $oldstring) =~ s/foo/bar/g;

Ce qui équivaut à:

my $newstring = $oldstring;
$newstring =~ s/foo/bar/g;

Alternativement, à partir de Perl 5.13.2, vous pouvez utiliser /rpour faire une substitution non destructive:

use 5.013;
#...
my $newstring = $oldstring =~ s/foo/bar/gr;
Tapoter
la source
3
Avez-vous oublié le gdans votre regex top?
mareoraft
23

Sous use strict, dites:

(my $new = $original) =~ s/foo/bar/;

au lieu.

Sam Kington
la source
10

La solution à une seule ligne est plus utile comme shibboleth que comme bon code; les bons codeurs Perl le sauront et le comprendront, mais c'est beaucoup moins transparent et lisible que le couplet de copie et de modification à deux lignes avec lequel vous commencez.

En d'autres termes, une bonne façon de faire est la façon dont vous le faites déjà . Une concision inutile au détriment de la lisibilité n'est pas une victoire.

Josh Millard
la source
Ah, mais la version à une ligne n'est pas sujette à l'erreur dans la question de la modification involontaire de la mauvaise chaîne.
ysth le
La version à une ligne, <i> si correctement exécutée </i>, n'est pas soumise, vrai. Mais c'est un problème distinct.
Josh Millard
9
Vous pourriez penser que c'est une concision inutile, mais devoir taper deux fois un nom de variable pour l'utiliser une fois est deux fois le nombre de points de défaillance. Il est parfaitement lisible pour les personnes qui connaissent la langue, et c'est même dans notre cours <i> Apprendre Perl </i>.
brian d foy
1

Une autre solution pré-5.14: http://www.perlmonks.org/?node_id=346719 (voir l'article de japhy)

Comme son approche l'utilise map, cela fonctionne également bien pour les tableaux, mais nécessite une cascade mappour produire un tableau temporaire (sinon l'original serait modifié):

my @orig = ('this', 'this sucks', 'what is this?');
my @list = map { s/this/that/; $_ } map { $_ } @orig;
# @orig unmodified
textral
la source
1

Je déteste foo et bar .. qui a imaginé ces termes non descriptifs dans la programmation de toute façon?

my $oldstring = "replace donotreplace replace donotreplace replace donotreplace";

my $newstring = $oldstring;
$newstring =~ s/replace/newword/g; # inplace replacement

print $newstring;
%: newword donotreplace newword donotreplace newword donotreplace
JoGotta
la source
2
En quoi est-ce différent de l'original? (Et je pense que vous voulez =~ s.)
Teepeemm
Erreur critique. La sortie réelle de ce code estnewword donotnewword newword donotnewword newword donotnewword
Pascal
2
Voyez ... si JoGotta avait utilisé le traditionnel et familier fooet bar, sa réponse aurait été exacte. Prouver, une fois de plus, que les coutumes existent pour une raison et que les leçons ne sont apprises qu'à la dure. ;)
Jon
-1

Si vous écrivez Perl avec use strict;, vous constaterez que la syntaxe d'une ligne n'est pas valide, même lorsqu'elle est déclarée.

Avec:

my ($newstring = $oldstring) =~ s/foo/bar/;

Vous obtenez:

Can't declare scalar assignment in "my" at script.pl line 7, near ") =~"
Execution of script.pl aborted due to compilation errors.

Au lieu de cela, la syntaxe que vous avez utilisée, bien qu'une ligne plus longue, est la manière syntaxiquement correcte de le faire use strict;. Pour moi, utiliser use strict;n'est plus qu'une habitude maintenant. Je le fais automatiquement. Tout le monde devrait.

#!/usr/bin/env perl -wT

use strict;

my $oldstring = "foo one foo two foo three";
my $newstring = $oldstring;
$newstring =~ s/foo/bar/g;

print "$oldstring","\n";
print "$newstring","\n";
Tim Kennedy
la source
1
Si vous use warnings;au lieu de -w, vous obtenez un meilleur contrôle: par exemple, si vous souhaitez désactiver temporairement les avertissements dans un bloc de code.
glenn jackman