2011-02-28 21 views
1

选项不要问为什么,但...如何使Perl的正则表达式条件

我有需要,如果在Windows上运行,但要区分大小写的正则表达式的情况下* nix上运行敏感。

下面是我现在正在做的一些事情的片段。

sub relative_path 
{ 
    my ($root, $path) = @_; 

    if ($os eq "windows") 
    { 
     # case insensitive with regex option 'i' 
     if ($path !~ /^\Q$root\E[\\\/](.*)$/i) 
     { 
      print "\tFAIL:$root not in $path\n"; 
     } 
     else 
     { 
      return $1; 
     } 
    } 
    else 
    { 
     # case sensitive 
     if ($path !~ /^\Q$root\E[\\\/](.*)$/) 
     { 
      print "\tFAIL:$root not in $path\n"; 
     } 
     else 
     { 
      return $1; 
     } 
    } 
    return ""; 
} 

唉!重复伤害我的强迫症,但我的perl-fu很弱。不知何故,我想让正则表达式选项'我'不区分大小写的条件,但我现在不怎么样?

+3

你可以以编程方式确定操作系统是否为windows:'my $ is_windows = $^O =〜/ mswin/i;' – 2011-02-28 19:05:41

回答

5

您可以创建模式和使用qr运营商将它们存储在标量:

sub relative_path 
{ 
    my ($root, $path) = @_; 

    my $pattern = ($os eq "windows") ? qr/^\Q$root\E[\\\/](.*)$/i : qr/^\Q$root\E[\\\/](.*)$/; 

    if ($path !~ $pattern) 
    { 
     print "\tFAIL:$root not in $path\n"; 
    } 
    else 
    { 
     return $1; 
    } 
} 

这可能不是100%完美,但希望你应该明白我的意思。

请务必查看章节"Quote and Quote-Like Operators" in perlop


编辑:好,这是一个干的解决方案,因为人们都在抱怨它。

sub relative_path 
{ 
    my ($root, $path) = @_; 

    my $base_pattern = qr/^\Q$root\E[\\\/](.*)$/; 
    my $pattern = ($os eq "windows") ? qr/$base_pattern/i : $base_pattern; 

    if ($path !~ $pattern) 
    { 
     print "\tFAIL:$root not in $path\n"; 
    } 
    else 
    { 
     return $1; 
    } 
} 
+0

我用'golf'定义了'$ pattern'定义逻辑 - 你可以使用if - 如果你愿意的话。在保存代码重复和LOC方面,你仍然会获得巨大的胜利。 – 2011-02-28 17:15:42

+0

阿哈的报价正则表达式像运营商是我失踪! http://perldoc.perl.org/perlop.html#Regexp-Quote-Like-Operators – 2011-02-28 17:20:02

+0

@Pev:是的。哎呀,我应该把你和那个部分联系起来:-) – 2011-02-28 17:28:23

9

您可以使用扩展构造来指定选项。例如:

#!/usr/bin/env perl 

use warnings; use strict; 

my $s = 'S'; 

print check($s, 'i'), "\n"; 
print check($s, '-i'), "\n"; 

sub check { 
    my ($s, $opt) = @_; 
    return "Matched" if $s =~ /(?$opt)^s\z/; 
    return "Did not match"; 
} 

请参阅perldoc perlre

+0

+1。如果你想有很多条件选项(除了区分大小写),这可能是更好/更具扩展性的答案。 – 2011-02-28 17:18:58

+2

因为DRY而+1。我想指出''(?p'在perl 5.8.9上不起作用,并且在该版本的'perlre'文件中没有提及它,它似乎在5.8.9没有'p'的情况下工作,然而,我的猜测是''?p'被添加到5.10中 – toolic 2011-02-28 18:00:13

+1

'(?p)'打开'/ p'修饰符,它保留匹配的字符串,使得'$ {^ PREMATCH}','$ {^ MATCH}'和'$ {^ POSTMATCH}'可在匹配后使用。在这种情况下,我看不出有什么理由让你打开它。只需使用'(?$ opt)'。 – cjm 2011-02-28 19:04:12

1

您也可以使用本地修饰符(perl的扩展正则表达式选项)它:

sub relative_path 
{ 
    my ($root, $path) = @_; 

    my $pattern = "^\Q$root\E[\\\/](.*)$"; 
    $pattern = "(?i)$pattern" if ($os eq "windows"); 
    if ($path =~ /$pattern/) 
    { 
     return $1; 
    } 
    else 
    { 
     print "\tFAIL:$root not in $path\n"; 
    } 
} 

(后我输入我的回答,我看到思南还建议,但我决定后我的回答为好,因为它给出了一个问题的答案)

+1

+1我没有使用OP的模式的原因很简单:我不明白它在做什么,我强烈怀疑有比使用正则表达式更好的方法来做到这一点;-) – 2011-03-01 03:08:25

+0

嘿,你是对的。我必须承认,我并没有真正尝试看待手头的问题。我只是重构了代码;)大多数时候这是一个有用的步骤,尽管当你的代码不够完整时,因为如果代码也很长并且重复,很难看出它的意图。我认为ikegami的解决方案看起来相当整洁:) – markijbema 2011-03-01 09:39:47

4

除了实现所述的目标,这正确地处理量与以前发布的正则表达式模式不同。

use Path::Class qw(dir); 

sub relative_path { 
    my ($root, $path) = @_; 

    if ($^O =~ /Win32/) { 
     require Win32; 
     $root = Win32::GetLongPathName($root); 
     $path = Win32::GetLongPathName($path); 
    } 

    $root = dir($root); 
    $path = dir($path); 

    if ($root->subsumes($path)) { 
     return $path->relative($root); 
    } else { 
     print "\tFAIL:$root not in $path\n"; 
     return ""; 
    } 
} 

顺便说一句,它不是很适合处理那里的错误。该函数应该返回一个错误信号(返回undef,抛出异常等),并且调用者应该按照它认为合适的方式处理它。分离关注。