perl 字符串连接和替换在一行中?
perl string catenation and substitution in a single line?
我需要修改一个包含文件路径的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/
将不变地通过
你可以这样做,但我不会说它更具可读性:
foreach ( ($config->{'backup_path'},
$config->{'work_path'},
$config->{'output_path'}
) ) {
( $_ = "/$_/" ) =~ s/\/{2,}/\//g;
}
好吧,重写你得到的东西:
my @vars = qw ( backup_path work_path output_path );
for ( @{$config}{@vars} ) {
s,^/*,/,; #prefix
s,/*$,/,; #suffix
s,/+,/,g; #double slashes anywhere else.
}
我要小心 - 针对魔术正则表达式进行优化并不是在所有情况下都有优势,因为它们很快变得不可读。
以上使用散列中的 hash slice mechanism 到 select 值(在本例中为引用),以及 s///
隐式操作 $_
的事实.并在修改时修改原始 var。
但是知道这一点也很有用,如果您正在对包含 /
的模式进行操作,那么切换定界符会很有帮助,因为那样您就不会获得 "leaning toothpicks" 效果。
s/\/{2,}/\//g
可以写成:
s,/+,/,g
或
s|/{2,}|/|g
如果你想保留数字量词,因为 +
本质上是 1 或更多,在这里工作相同,因为它无论如何都会将双精度折叠成单精度,但它 技术上 匹配 /
(并将其替换为 /
),而原始模式不匹配。但是,出于同样的原因,如果您的模式中有 ,
,您就不想使用它。
不过我认为这可以解决问题;
s,(?:^/*|\b\/*$|/+),/,g for @{$config}{qw ( backup_path work_path output_path )};
这匹配交替分组,替换:
- 行首,零个或多个
/
- 字边界,零个或多个
/
行尾
- 在其他任何地方使用一个或多个斜杠。
与单个 /
。
如上使用hash slice机制,但没有中间'vars'。
(出于某种原因,如果没有单词边界 \b
零宽度锚点,第二个分组将无法正常工作 - 我认为这是一个回溯问题,但我不完全确定)
对于奖励积分 - 如果您的源数据结构合适,您可能 select @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/'
};
这个问题已经有很多很棒的答案了。
从非 perl 专家(我)的角度来看,有些难以阅读/理解。 ;)
所以,我可能会使用这个:
my @vars = qw ( backup_path work_path output_path );
for my $var (@vars) {
my $value = '/' . $config->{$var} . '/';
$value =~ s|//+|/|g;
$config->{$var} = $value;
}
对我来说,这也是一年后可读的。 :)
我需要修改一个包含文件路径的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/
将不变地通过
你可以这样做,但我不会说它更具可读性:
foreach ( ($config->{'backup_path'},
$config->{'work_path'},
$config->{'output_path'}
) ) {
( $_ = "/$_/" ) =~ s/\/{2,}/\//g;
}
好吧,重写你得到的东西:
my @vars = qw ( backup_path work_path output_path );
for ( @{$config}{@vars} ) {
s,^/*,/,; #prefix
s,/*$,/,; #suffix
s,/+,/,g; #double slashes anywhere else.
}
我要小心 - 针对魔术正则表达式进行优化并不是在所有情况下都有优势,因为它们很快变得不可读。
以上使用散列中的 hash slice mechanism 到 select 值(在本例中为引用),以及 s///
隐式操作 $_
的事实.并在修改时修改原始 var。
但是知道这一点也很有用,如果您正在对包含 /
的模式进行操作,那么切换定界符会很有帮助,因为那样您就不会获得 "leaning toothpicks" 效果。
s/\/{2,}/\//g
可以写成:
s,/+,/,g
或
s|/{2,}|/|g
如果你想保留数字量词,因为 +
本质上是 1 或更多,在这里工作相同,因为它无论如何都会将双精度折叠成单精度,但它 技术上 匹配 /
(并将其替换为 /
),而原始模式不匹配。但是,出于同样的原因,如果您的模式中有 ,
,您就不想使用它。
不过我认为这可以解决问题;
s,(?:^/*|\b\/*$|/+),/,g for @{$config}{qw ( backup_path work_path output_path )};
这匹配交替分组,替换:
- 行首,零个或多个
/
- 字边界,零个或多个
/
行尾 - 在其他任何地方使用一个或多个斜杠。
与单个 /
。
如上使用hash slice机制,但没有中间'vars'。
(出于某种原因,如果没有单词边界 \b
零宽度锚点,第二个分组将无法正常工作 - 我认为这是一个回溯问题,但我不完全确定)
对于奖励积分 - 如果您的源数据结构合适,您可能 select @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/'
};
这个问题已经有很多很棒的答案了。
从非 perl 专家(我)的角度来看,有些难以阅读/理解。 ;)
所以,我可能会使用这个:
my @vars = qw ( backup_path work_path output_path );
for my $var (@vars) {
my $value = '/' . $config->{$var} . '/';
$value =~ s|//+|/|g;
$config->{$var} = $value;
}
对我来说,这也是一年后可读的。 :)