#! /usr/bin/perl
use warnings;
use strict;
my %replace = (
"foo" => "baz",
"bar" => "quux",
);
my $to_replace = qr/@{["(" .
join("|" => map quotemeta($_), keys %replace) .
")"]}/;
while (<DATA>) {
s/$to_replace/$replace{$1}/g;
print;
}
__DATA__
The food is under the bar in the barn.
Бит @{[...]}
может выглядеть странно.Это взломать интерполяцию сгенерированного контента внутри кавычек и операторов, подобных кавычкам .Результат join
попадает внутрь анонимного конструктора ссылки на массив []
и немедленно разыменовывается благодаря @{}
.
Если все это кажется слишком вонючим, это то же самое, что
my $search = join "|" => map quotemeta($_), keys %replace;
my $to_replace = qr/($search)/;
минус временная переменная.
Обратите внимание на использование quotemeta
- спасибо Ивану! - который экранирует первую строку каждой пары, поэтому механизм регулярных выражений будет обрабатывать ихв виде буквенных строк.
Вывод:
The bazd is under the quux in the quuxn.
Метапрограммирование, то есть написание программы, которая пишет другую программу, также хорошо.Начало выглядит знакомым:
#! /usr/bin/perl
use warnings;
use strict;
use File::Compare;
die "Usage: $0 path ..\n" unless @ARGV >= 1;
# stub
my @pairs = (
["foo" => "baz"],
["bar" => "quux"],
['foo$bar' => 'potrzebie\\'],
);
Теперь мы создаем программу, которая выполняет все s///
замены, но является quotemeta
на стороне замены хорошей идеей? -
my $code =
"sub { while (<>) { " .
join(" " => map "s/" . quotemeta($_->[0]) .
"/" . quotemeta($_->[1]) .
"/g;",
@pairs) .
"print; } }";
#print $code, "\n";
и скомпилируйте его с помощью eval
:
my $replace = eval $code
or die "$0: eval: $@\n";
Для замены мы используем готовый Perl редактор для редактирования на месте :
# set up in-place editing
$^I = ".bak";
my @save_argv = @ARGV;
$replace->();
Ниже приведена дополнительная аккуратность, которая восстанавливает резервные копии, которые, по мнению модуля File :: Compare , не нужны:
# in-place editing is conservative: it creates backups
# regardless of whether it modifies the file
foreach my $new (@save_argv) {
my $old = $new . $^I;
if (compare($new, $old) == 0) {
rename $old => $new
or warn "$0: rename $old => $new: $!\n";
}
}