正则表达式 - 零宽度'字边界'使交替模式正确匹配

时间:2017-04-05 15:24:21

标签: regex perl

参考:perl string catenation and substitution in a single line?

给出输入:

home/////test/tmp/

并希望转变为:

/home/test/tmp/

(和其他文件路径一样的模式,需要尾随和前导斜杠,但没有双精度。例如/home/test/tmp/通过,但/home/test/tmp得到一个尾随斜杠等。)

使用三重正则表达式;

s,^/*,/,;  #prefix
s,/*$,/,; #suffix
s,/+,/,g; #double slashes anywhere else. 

给我们正确的结果:

#!/usr/bin/env perl

use strict;
use warnings;

my $str = 'home/////teledyne/tmp/';
$str =~ s,^/*,/,;    #prefix
$str =~ s,/*$,/,;    #suffix
$str =~ s,/+,/,g;    #double slashes anywhere else.
print $str; 

但如果我尝试使用交替组合这些模式,我会得到:

s,(^/*|/+|/*$),/,g 

看起来应该工作......它实际上没有,我得到一个双尾斜线。

但是添加零宽度匹配,它可以正常工作:

s,(^/*|/+|\b/*$),/,g;

任何人都可以帮助我理解交替小组中发生的事情会有什么不同,并且只有将\b留在那里可能会有问题吗?

2 个答案:

答案 0 :(得分:2)

原因是/+下的/g交替与最后一个斜线匹配 - 然后由于锚的存在而继续搜索。它从最后一次替换后的位置继续,因此在最后一次斜线之后。该搜索在$处匹配零斜杠并添加/

我们可以通过

看到这一点
perl -wE'
    $_ = "home/dir///end/"; 
    while (m{( ^/* | /+ | /*$ )}gx) { say "Got |$1| at ", pos }
'

打印(对齐at ...以便于阅读)

Got ||    at 0
Got |/|   at 5
Got |///| at 11
Got |/|   at 15
Got ||    at 15

实际替换

s{( ^/* | /+ | /*$ )}{ say "Got |$1| at ", pos; q(/) }egx

数字不同,因为它们指的是中间字符串中的位置,其中最后两个

...
Got |/| at 14
Got ||  at 15

正在讲述。

我没有看到\b出现问题,如问题或/*\b$

这是一个有趣的问题,但我想补充一点,

可以避免所有这些细节
$_ = '/' . (join '/', grep { /./ } split '/', $_) . '/'  for @paths;

答案 1 :(得分:0)

这是一个单一的正则表达式:

s='home/////test/tmp/'
perl -pe 's~^(?!/)|(?<!/)$|/{2,}~/~g' <<< "$s"
/home/test/tmp/

s='home/test/tmp'
perl -pe 's~^(?!/)|(?<!/)$|/{2,}~/~g' <<< "$s"
/home/test/tmp/

正则表达式分手:

^(?!/) # Line start if not followed by /
|
(?<!/)$ # Line end if not preceded by /
|
/{2,} # 2 or more /