Bagaimana cara saya melakukan substitusi Perl pada string sambil mempertahankan yang asli?

180

Di Perl, apa cara yang baik untuk melakukan penggantian pada string menggunakan ekspresi reguler dan menyimpan nilai dalam variabel yang berbeda, tanpa mengubah yang asli?

Saya biasanya hanya menyalin string ke variabel baru kemudian mengikatnya ke s///regex yang melakukan penggantian pada string baru, tetapi saya bertanya-tanya apakah ada cara yang lebih baik untuk melakukan ini?

$newstring = $oldstring;
$newstring =~ s/foo/bar/g;
kaybenleroll
sumber

Jawaban:

257

Ini adalah ungkapan yang selalu saya gunakan untuk mendapatkan salinan string yang dimodifikasi tanpa mengubah yang asli:

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

Dalam perl 5.14.0 atau lebih baru, Anda dapat menggunakan /r pengubah substitusi non-destruktif baru :

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

Catatan: Solusi di atas juga berfungsi tanpa g. Mereka juga bekerja dengan pengubah lainnya.

John Siracusa
sumber
6
Baik digunakan ketat atau tidak. Ruang lingkup variabel minimum ++
ysth
Saya bertanya-tanya apakah sesuatu seperti my $new = $_ for $old =~ s/foo/bar;akan bekerja?
Benoit
2
@Enoit, saya yakin maksud Anda s/foo/bar/ for my $newstring = $oldstring;ini berhasil, tapi jauh lebih aneh.
ikegami
43

Pernyataan:

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

Yang setara dengan:

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

Atau, pada Perl 5.13.2 Anda dapat menggunakan /runtuk melakukan subtitusi yang tidak merusak:

use 5.013;
#...
my $newstring = $oldstring =~ s/foo/bar/gr;
Menepuk
sumber
3
Apakah Anda lupa gdi regex teratas Anda?
mareoraft
23

Di bawah use strict, katakan:

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

sebagai gantinya.

Sam Kington
sumber
10

Solusi one-liner lebih bermanfaat sebagai shibboleth daripada kode yang baik; coders Perl yang baik akan mengetahuinya dan memahaminya, tapi itu jauh lebih transparan dan mudah dibaca daripada dua baris salin dan modifikasi bait yang Anda mulai.

Dengan kata lain, cara yang baik untuk melakukan ini adalah cara Anda sudah melakukannya. Keputusan yang tidak perlu dengan biaya keterbacaan bukanlah kemenangan.

Josh Millard
sumber
Ah, tetapi versi satu baris tidak mengalami kesalahan dalam pertanyaan memodifikasi string yang salah secara tidak sengaja.
ysth
Versi satu baris, <i> jika dijalankan dengan benar </i>, tidak tunduk, benar. Tapi itu masalah tersendiri.
Josh Millard
9
Anda mungkin berpikir itu adalah keputusan yang tidak perlu, tetapi harus mengetikkan nama variabel dua kali untuk menggunakannya sekali adalah dua kali jumlah titik kegagalan. Ini mudah dibaca oleh orang-orang yang tahu bahasa itu, dan itu bahkan dalam kursus <i> Learning Perl kami. "
brian d foy
1

Solusi pra-5.14 lainnya: http://www.perlmonks.org/?node_id=346719 (lihat posting japhy)

Saat pendekatannya digunakan map, ini juga berfungsi dengan baik untuk array, tetapi membutuhkan kaskade mapuntuk menghasilkan array sementara (jika tidak, yang asli akan dimodifikasi):

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

Saya benci foo and bar .. siapa sih yang memimpikan istilah non deskriptif ini dalam pemrograman?

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
sumber
2
Apa bedanya dengan yang asli? (Dan saya pikir Anda mau =~ s.)
Teepeemm
Kesalahan Clbuttic. Output aktual dari kode itu adalahnewword donotnewword newword donotnewword newword donotnewword
Pascal
2
Lihat ... jika JoGotta menggunakan yang tradisional dan familiar foodan bar, jawabannya pasti akurat. Membuktikan, sekali lagi, adat ada karena suatu alasan dan pelajaran hanya dipelajari dengan cara yang sulit. ;)
Jon
-1

Jika Anda menulis Perl dengan use strict;, maka Anda akan menemukan bahwa sintaks satu baris tidak valid, bahkan ketika dideklarasikan.

Dengan:

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

Anda mendapatkan:

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

Alih-alih, sintaksis yang telah Anda gunakan, sementara sebuah baris lebih panjang, adalah cara yang benar secara sintaksis untuk melakukannya use strict;. Bagi saya, menggunakan use strict;hanyalah kebiasaan sekarang. Saya melakukannya secara otomatis. Semua orang harus.

#!/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
sumber
1
Jika Anda use warnings;bukannya -w, Anda mendapatkan kontrol yang lebih besar: misalnya, jika Anda ingin mematikan sementara peringatan di blok kode.
glenn jackman