Jak wykonać podstawienie Perla na łańcuchu, zachowując oryginał?

180

W Perlu jaki jest dobry sposób na wykonanie zamiany na łańcuch przy użyciu wyrażenia regularnego i zapisanie wartości w innej zmiennej bez zmiany oryginału?

Zwykle po prostu kopiuję ciąg do nowej zmiennej, a następnie wiążę go z s///wyrażeniem regularnym, które zastępuje nowy ciąg, ale zastanawiałem się, czy istnieje lepszy sposób, aby to zrobić?

$newstring = $oldstring;
$newstring =~ s/foo/bar/g;
kaybenleroll
źródło

Odpowiedzi:

257

To jest idiom, którego zawsze używałem, aby uzyskać zmodyfikowaną kopię łańcucha bez zmiany oryginału:

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

W perlu 5.14.0 lub nowszym możesz użyć nowego /r nieniszczącego modyfikatora podstawienia :

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

Uwaga: powyższe rozwiązania działają bez g. Działają również z innymi modyfikatorami.

John Siracusa
źródło
6
Bez względu na to, czy jest w użyciu, czy nie. Minimalny zakres zmiennych ++
ysth
Zastanawiałem się, czy coś takiego my $new = $_ for $old =~ s/foo/bar;zadziała?
Benoit,
2
@Benoit, myślę, że masz na myśli s/foo/bar/ for my $newstring = $oldstring;To działa, ale jest znacznie dziwniejsze.
ikegami
43

Twierdzenie:

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

Co odpowiada:

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

Alternatywnie, począwszy od Perla 5.13.2, możesz użyć, /raby dokonać nieniszczącej zamiany:

use 5.013;
#...
my $newstring = $oldstring =~ s/foo/bar/gr;
Poklepać
źródło
3
Zapomniałeś gw swoim regexie?
mareoraft,
23

Poniżej use strictpowiedz:

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

zamiast.

Sam Kington
źródło
10

Jednoliniowe rozwiązanie jest bardziej przydatne jako shibboleth niż dobry kod; dobrzy koderzy Perla znają go i rozumieją, ale jest on znacznie mniej przejrzysty i czytelny niż dwuliniowy dwuwierszowy kopiuj i modyfikuj kuplet, od którego zaczynasz.

Innymi słowy, dobrym sposobem na to jest sposób, w jaki już to robisz. Niepotrzebne zawarcie umowy kosztem czytelności nie jest wygraną.

Josh Millard
źródło
Ach, ale wersja z jednym wierszem nie podlega błędowi w kwestii niezamierzonej modyfikacji niewłaściwego ciągu.
ysth
Wersja jednowierszowa, <i> jeśli poprawnie wykonana </i>, nie jest przedmiotem, prawda. Ale to osobny problem.
Josh Millard
9
Może się wydawać, że jest to niepotrzebna zwartość, ale konieczność dwukrotnego wpisania nazwy zmiennej, aby użyć jej raz, jest dwukrotnością liczby punktów awarii. Jest doskonale czytelny dla osób znających język, a nawet na naszym kursie <i> Learning Perl </i>.
brian d foy,
1

Inne rozwiązanie sprzed 5.14: http://www.perlmonks.org/?node_id=346719 (patrz post japhy)

Zgodnie z jego podejściem mapdziała również dobrze dla tablic, ale wymaga kaskadowania w mapcelu utworzenia tablicy tymczasowej (w przeciwnym razie oryginał zostałby zmodyfikowany):

my @orig = ('this', 'this sucks', 'what is this?');
my @list = map { s/this/that/; $_ } map { $_ } @orig;
# @orig unmodified
textral
źródło
1

Nienawidzę foo i bar .. kto marzył o tych nieopisowych terminach w programowaniu?

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
źródło
2
Czym różni się to od oryginału? (I myślę, że chcesz =~ s.)
Teepeemm,
Błędny błąd. Rzeczywiste wyjście tego kodu tonewword donotnewword newword donotnewword newword donotnewword
Pascal
2
Zobacz ... gdyby JoGotta użył tradycyjnego i znanego, fooa barjego odpowiedź byłaby trafna. Po raz kolejny udowadnianie, że istnieją zwyczaje, a lekcje wyciągane są tylko na trudny sposób. ;)
Jon
-1

Jeśli napiszesz Perla use strict;, przekonasz się, że składnia jednowierszowa jest nieprawidłowa, nawet jeśli została zadeklarowana.

Z:

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

Dostajesz:

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

Zamiast tego używana przez ciebie składnia, choć dłuższa linia, jest poprawnym składniowo sposobem na zrobienie tego use strict;. Dla mnie używanie use strict;jest teraz tylko nawykiem. Robię to automatycznie. Każdy powinien.

#!/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
źródło
1
Jeśli use warnings;zamiast tego -wzyskasz większą kontrolę: na przykład, jeśli chcesz tymczasowo wyłączyć ostrzeżenia w bloku kodu.
glenn jackman