perl字符串连接和单行替换?

时间:2017-04-04 15:48:39

标签: regex string perl premature-optimization

我需要修改包含文件路径的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 个答案:

答案 0 :(得分:2)

好吧,只需改写你得到的东西:

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从散列中选择值(在本例中为引用),以及s///隐含地在$_上操作的事实。并修改原始var。

但是,如果您对包含/的模式进行操作,那么切换分隔符会有所帮助,这也很有用,因为这样您就不会获得“倾斜的牙签”效果。

s/\/{2,}/\//g可以写成:

s,/+,/,g

 s|/{2,}|/|g

如果你想保留数字量词,因为+本来就是1或者更多,它在这里工作相同,因为它无论如何都会将双折叠成一个,但技术上匹配/(并将其替换为/)原始模式没有。但出于同样的原因,如果您的模式中有,,则不希望使用s,(?:^/*|\b\/*$|/+),/,g for @{$config}{qw ( backup_path work_path output_path )};

但是我觉得这样做很有效;

/

这匹配交替分组,替换:

  • 行首,零或多个/
  • 字边界,零或更多/行尾
  • 其他任何地方的一个或多个斜杠。

只有一个\b

使用上面的散列切片机制,但没有中间的'vars'。

(由于某种原因,没有单词边界@vars零宽度锚点,第二个分组无法正常工作 - 我认为这是一个回溯问题,但我不完全确定)

对于奖励积分 - 如果您的源数据结构合适,您可以使用grep选择my @vars = grep { /_path$/ } keys %$config; #etc. Or inline with: s,(?:^/*|\b\/*$|/+),/,g for @{$config}{grep { /_path$/ } keys %$config };

s|(?:/|\A|\z)/*|/|

编辑:或Borodin注释:

#!/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/'
        };

结果:

/node_modules/protractor/built/cli.js

答案 1 :(得分:1)

你可以这样做,但我不会把它称为更具可读性:

foreach ( ($config->{'backup_path'},
           $config->{'work_path'},
           $config->{'output_path'}
         ) ) {
     ( $_ = "/$_/" ) =~ s/\/{2,}/\//g;
}

答案 2 :(得分:0)

这个问题已经得到了很多很棒的答案。

从非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;
}

对我来说,这也是一年后可读的。 :)