Слияние строк Perl и подстановка в одну строку?
Мне нужно изменить переменную perl, содержащую путь к файлу; он должен начинаться и заканчиваться косой чертой (/), и все экземпляры нескольких косых черт должны быть уменьшены до одной косой черты.
(Это связано с тем, что существующий процесс не обеспечивает согласованный синтаксис конфигурации, поэтому повсюду разбросаны сотни файлов конфигурации, которые могут иметь или не иметь косые черты в нужных местах имен файлов и путей.)
Что-то вроде этого:
foreach ( ($config->{'backup_path'},
$config->{'work_path'},
$config->{'output_path'}
) ) {
$_ = "/" . $_ . "/";
$_ =~ s/\/{2,}/\//g;
}
но это не выглядит оптимальным или особенно читабельным для меня; Я предпочел бы иметь более элегантное выражение (если оно закончится использованием необычного регулярного выражения, я воспользуюсь комментарием, чтобы сделать его более понятным).
Примеры ввода и вывода
home/datamonster//c2counts
становится /home/datamonster/c2counts/
home/////teledyne/tmp/
становится /home/teledyne/tmp/
а также /var/backup/DOC/all_instruments/
пройдет без изменений
3 ответа
Ну, просто переписать то, что вы получили:
my @vars = qw ( backup_path work_path output_path );
for ( @{$config}{@vars} ) {
s,^/*,/,; #prefix
s,/*$,/,; #suffix
s,/+,/,g; #double slashes anywhere else.
}
Я был бы осторожен - оптимизация для магических регулярных выражений не является преимуществом в каждой ситуации, потому что они становятся довольно быстро нечитаемыми.
Выше используется механизм среза хеша для выбора значений из хеша (ссылка в данном случае) и тот факт, что s///
неявно действует на $_
тем не мение. И изменяет оригинальный var, когда это делает.
Но также полезно знать, работаете ли вы с шаблонами, содержащими /
полезно поменять разделители, потому что таким образом вы не получите эффект "наклоняющиеся зубочистки".
s/\/{2,}/\//g
можно записать как:
s,/+,/,g
или же
s|/{2,}|/|g
если вы хотите сохранить числовой квантификатор, как +
по сути 1 или более, который работает здесь одинаково, потому что он в любом случае сворачивает двойное число в единичное, но технически соответствует /
(и заменяет его на /
), где оригинальный шаблон не. Но вы не хотели бы использовать ,
если у вас есть это в вашем шаблоне, по той же причине.
Однако я думаю, что это делает трюк;
s,(?:^/*|\b\/*$|/+),/,g for @{$config}{qw ( backup_path work_path output_path )};
Это соответствует альтернативной группе, заменяющей либо:
- начало строки, ноль или более
/
- граница слова, ноль или более
/
конец линии - одна или несколько косых черт в другом месте.
с одним /
,
использует механизм среза хеша, как описано выше, но без промежуточных 'vars'.
(По какой-то причине вторая группа не работает правильно без границы слова \b
якорь нулевой ширины - я думаю, что это проблема возврата, но я не совсем уверен)
Для бонусных баллов - вы можете выбрать @vars
с помощью grep
если ваша исходная структура данных соответствует:
my @vars = grep { /_path$/ } keys %$config;
#etc. Or inline with:
s,(?:^/*|\b\/*$|/+),/,g for @{$config}{grep { /_path$/ } keys %$config };
Редактировать: Или, как отмечает Borodin:
s|(?:/|\A|\z)/*|/|
Давать нам:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my $config = {
backup_path => "/fish/",
work_path => "narf//zoit",
output_path => "/wibble",
test_path => 'home/datamonster//c2counts',
another_path => "/home/teledyne/tmp/",
again_path => 'home/////teledyne/tmp/',
this_path => '/var/backup/DOC/all_instruments/',
};
s,(?:/|\A|\b\z)/*,/,g for @{$config}{grep { /_path$/ } keys %$config };
print Dumper $config;
Результаты:
$VAR1 = {
'output_path' => '/wibble/',
'this_path' => '/var/backup/DOC/all_instruments/',
'backup_path' => '/fish/',
'work_path' => '/narf/zoit/',
'test_path' => '/home/datamonster/c2counts/',
'another_path' => '/home/teledyne/tmp/',
'again_path' => '/home/teledyne/tmp/'
};
Вы могли бы сделать это так, но я бы не назвал это более читабельным:
foreach ( ($config->{'backup_path'},
$config->{'work_path'},
$config->{'output_path'}
) ) {
( $_ = "/$_/" ) =~ s/\/{2,}/\//g;
}
Этот вопрос уже получил много фантастических ответов.
С точки зрения non-perl-expert (me), некоторые трудно читать / понимать.;)
Итак, я бы, вероятно, использовал это:
my @vars = qw ( backup_path work_path output_path );
for my $var (@vars) {
my $value = '/' . $config->{$var} . '/';
$value =~ s|//+|/|g;
$config->{$var} = $value;
}
Для меня это будет читаться через год тоже.:)