【问题标题】:How can I combine overlapping path segments to get the full path in Perl?如何组合重叠的路径段以获得 Perl 中的完整路径?
【发布时间】:2010-11-28 09:52:52
【问题描述】:

我真的不喜欢 Perl,但我必须将它用于我当前的任务。

这是我的问题...我有三个字符串,它们构成完整目录路径的元素(Windows,但也需要在 *nix 上工作)。比如……

$root = "c:\\checkout\\omega";
$client = "abc\\mainline";
$build = "omega\\abc\\mainline\\host\\make";

我想将这些组合成一个完整的路径,例如

"c:\\checkout\\omega\\abc\\mainline\\host\\make" 

但 $build 字符串与 $root 和/或 $client 字符串之间存在重叠。我如何将这些组合起来以获得完整路径并忽略重叠。 $client 在此示例中可能可以忽略,但在其他情况下,$build may$client 重叠而不是 $root

我可以想到很多可怕的混乱方式来实现它,但我假设(也许是错误的)有一种简单、干净甚至优雅的方式来实现它,因为 Perl 主要是关于文本操作。

可能是某种字符串或运算。我愚蠢地尝试了......

($root . $client) | $build 

但它是按位运算,结果是垃圾!

【问题讨论】:

  • 文字斜线不会造成问题吗?并不是说您可以在 Windows 上使用“/”作为目录分隔符: $root = "c:/checkout/omega"; $client = "abc/主线"; $build = "omega/abc/mainline/host/make";
  • 目前双引号中的\是转义字符。您需要转义它们 \\ 或改用单引号(或 q{})。就像 Pod 说的,你可能会发现在 Windows 上使用 / 可以正常工作。
  • 这个问题不清楚,但如果你能不那么模棱两可的话,很可能会有一个简单的答案。你到底想做什么?在您的示例中,您没有组合所有三个路径元素。你能解释一下什么时候应该组合元素,什么时候不应该组合?
  • 您可能想要使用 File::Spec,但您能进一步解释一下吗?我认为你的意思是你需要删除路径组件直到并包括 $build 中的 $client (给你留下类似 'host\make' 的东西,然后将剩下的部分与 $root 和 $client 结合起来得到类似 '$ root\$client\$remainder'。对吗?
  • 噢!是的,'\\' 表示斜线!

标签: perl string path


【解决方案1】:

下面的这个正则表达式更适合消除重复的路径序列。

qr{ ( 
      [\\/]  # 1. starts with a path break
      .+?    # 2. whatever
    )
    \1       # whatever was captured in the previous group 
             # it forces us to backtrack on #2 until we have duplicates
             # it will necessarily have a path break at the beginning
  }x;

只要路径中没有重复的字母,Dave Webb 提供的正则表达式就可以工作。只需创建最后一个节点'mmake',它就会中断。

我明白了:

original c:\checkout\omega\abc\mainline\omega\abc\mainline\host\mmake
overlap m
new c:\checkout\omega\abc\mainline\omega\abc\mainline\host\make

您希望重复是目录名称,而不是字符。

还需要一个简单的替换。当您在正则表达式中看到 ^.*.*$ 时,可能不需要它。在这个中不再需要它了。

事实上,所有这些都可以通过以下方式完成:

$path =~ s/([\\\/]+.+?)\1/$1/;

替换一些东西,它与那个东西重复。

File::Spec

顺便说一句,File::Spec 是以独立于平台的方式连接目录的公认方式:

my $path = File::Spec->catfile( $root, $client, $build );
$path =~ s/([\\\/]+.+?)\1/$1/;

不过,我对File::Spec 有一点小烦恼。我喜欢/ 用于目录。并且 perl works 在 windows 环境中与 / 一起使用。只要我停留在 perl 的范围内,我就不必用 escape 字符(在 C 语言系列中)分隔路径。 File::Spec 强制反斜杠与 Windows 平台一致。

但是,如果这就是您要寻找的东西,那可能是使用它的更多理由。

【讨论】:

  • 使用 / 不会强制使用反斜杠...您可以使用其他标点符号作为正则表达式分隔符。例如,s|(/.+)\1|$1|、s{(/.+)\1}{$1} 等
  • @Dave Sherohman:我澄清了File::Spec 强制使用反斜杠。
【解决方案2】:

你有三个路径的事实让我有点困惑,但如果你想找到两个之间的重叠,你可以在正则表达式中使用 back reference

例如:

$root = "c:\\checkout\\omega";
$build = "omega\\abc\\mainline\\host\\make";    

# Concatenate Strings
$path = "$root\\$build";
print "original ",$path,"\n";

# Look for overlap using a backreference
$path =~ /^.*(.+)\1.*$/;
print "overlap ",$1,"\n";

# Do a substitution to remove the overlap
$path =~ s/^(.*)(.+)\2(.*)$/\1\2\3/;
print "new ",$path,"\n";

这将产生以下输出:

original c:\checkout\omega\omega\abc\mainline\host\make
overlap omega\
new c:\checkout\omega\abc\mainline\host\make

【讨论】:

  • 寻找重叠的正则表达式最好是 /(.+)\1/。默认情况下,正则表达式将匹配字符串中的任何位置,因此以(非捕获)^.* 开头或以 .*$ 结尾只是毫无意义的噪音。根据您的正则表达式实现,这些也可以通过强制回溯来显着减慢速度,但我很确定 Perl 实现已经过优化以避免性能下降。
  • 想一想...您不需要在最终的正则表达式中重复搜索。将其更改为 s/$1// 可能会正常工作 - 重复的部分位于前一个正则表达式的 $1 中,因此只需将其替换为任何内容。只要您不包含 /g 修饰符,它将仅替换重复文本的第一个实例。或者使用 s/(.+)\1/$1/ 一次性完成所有操作。 (根据“使用警告”,在 s/// 的替换部分使用 \1 “最好写成 $1”。)
【解决方案3】:

这是一种方法:

  1. 使用单引号保留反斜杠(如果您想要反斜杠);

  2. 我的 $fullpath = join "", $root, $client, $build;

join 是字符串之间的“胶水”——在这种情况下,为空或什么都没有。

以上给出:

c:\checkout\omegaabc\mainlineomega\abc\mainline\host\make

因此,如果您需要在字符串之间使用反斜杠,请改用join "\\",这将给出:

c:\checkout\omega\abc\mainline\omega\abc\mainline\host\make

使用双引号中的字符串,\ 后面的字符将被转义。在单引号中,文字字符串被保留。

然后您可以轻松地将反斜杠转换为 (*nix) 正斜杠,但这是另一个过程。对于 Perl,请始终使用use strict;,当您从命令行运行它时,这将有助于指出任何潜在的故障。

【讨论】:

    【解决方案4】:

    我能想到很多可怕的混乱 实现它的方法,但我认为 (也许是错误的)有一个 简单,干净,甚至优雅 这样做的方法

    是的。看看Path::Class 模块。从您的问题来看,您要做什么并不完全清楚,但Class::Path 让您以跨平台的方式操作路径。

    【讨论】:

      【解决方案5】:

      不知道您的结构是否有任何规则(例如,在您的示例中 $client 似乎是多余的?)但如果有,那么您可以执行以下操作:

      my $root  = 'c:\checkout\omega'; 
      my $build = 'omega\abc\mainline\host\make';
      
      # $root + $build minus first node
      my $file = join '\\', $root, ( split /\\/, $build, 2 )[1];
      

      【讨论】:

        猜你喜欢
        • 2011-01-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-22
        • 1970-01-01
        • 2017-03-16
        • 2014-12-14
        相关资源
        最近更新 更多