文字列内の複数の文字列を置き換える方法はありますか?たとえば、文字列hello world what a lovely day
そしてwhat
とlovely
を別のものに置き換えたい。
$sentence = "hello world what a lovely day";
@list = ("what", "lovely"); # strings to replace
@replist = ("its", "bad"); # strings to replace with
($val = $sentence) =~ "tr/@list/@replist/d";
print "$val\n"; # should print "hello world its a bad day"..
それが機能しない理由はありますか?
ありがとう。
まず第一に、tr
はそのようには機能しません。詳細についてはperldoc perlop
を参照してください。ただし、tr
は音訳を行い、置換とは大きく異なります。
この目的のために、置き換えるより正しい方法は
# $val
$val =~ s/what/its/g;
$val =~ s/lovely/bad/g;
「同時」変更はかなり難しいことに注意してください。ただし、たとえば、
%replacements = ("what" => "its", "lovely" => "bad");
($val = $sentence) =~ s/(@{[join "|", keys %replacements]})/$replacements{$1}/g;
(もちろん、文字列をメタ文字に置き換えるには、エスケープが必要になる場合があります。)
これは、用語の非常に緩い意味でまだ同時ですが、ほとんどの目的で、置換が1つのパスで行われるかのように機能します。
また、"what"
ではなく"it's"
を"its"
に置き換える方が正しいです。
tr///d
はリクエストとは関係がないため、主に機能していません(tr/abc/12/d
はaを1に置き換え、bを2に置き換え、cを削除します)。また、デフォルトでは、配列はタスクに役立つ方法で正規表現に補間されません。また、ハッシュルックアップやサブルーチン呼び出しなどのロジックがないと、s///
操作の右側で決定を下すことはできません。
タイトルの質問に答えるために、次の方法で複数の置換を同時に(つまり、便利な連続で)実行できます。
#! /usr/bin/env Perl
use common::sense;
my $sentence = "hello world what a lovely day";
for ($sentence) {
s/what/it's/;
s/lovely/bad/
}
say $sentence;
ここで試みるようなことを行うには:
#! /usr/bin/env Perl
use common::sense;
my $sentence = "hello world what a lovely day";
my %replace = (
what => "it's",
lovely => 'bad'
);
$sentence =~ s/(@{[join '|', map { quotemeta($_) } keys %replace]})/$replace{$1}/g;
say $sentence;
このような置換を多数行う場合は、最初に正規表現を「コンパイル」してください。
my $matchkey = qr/@{[join '|', map { quotemeta($_) } keys %replace]}/;
...
$sentence =~ s/($matchkey)/$replace{$1}/g;
編集:
そして、配列補間についての私の意見を拡張するために、$"
を変更することができます。
local $" = '|';
$sentence =~ s/(@{[keys %replace]})/$replace{$1}/g;
# --> $sentence =~ s/(what|lovely)/$replace{$1}/g;
配列にすでにキーがある場合はそうかもしれませんが、これは実際にはここでは改善されません。
local $" = '|';
$sentence =~ s/(@keys)/$replace{$1}/g;