将glob转换为regex或使用Perl处理glob模式

时间:2011-07-07 19:41:25

标签: regex perl glob

我有一个config .ini文件,用户可以使用Perl正则表达式或Ant globbing模式指定文件模式。例如,以下内容将禁止用户创建Windows下不允许的文件:

[BAN Defined using Ant Globbing]
file = **/prn.*
ignorecase = true

[BAN Defined using Regular expressions]
match = /(aux|con|com[0-9]*|lpt[0-9]*|nul|clock$)\.?[a-z]$
ignorecase = true

现在,我必须将glob转换为正则表达式,以便以编程方式处理它。我有一个例程,但它有点复杂。我正在寻找以下其中一项:

  • 将glob转换为正则表达式的简便方法
  • 像正则表达式一样匹配glob表达式的方法。

例如:

 if ($regex =~ /\/(aux|con|com[0-9]*|lpt[0-9]*|nul|clock$)\.?[a-z]$) {
 if ($glob ?magic? /**/prn.*/) {

我希望有一些神奇的Perl方式来做到这一点。那么,有一个简单的不容错过的方法:

顺便说一句,这是我的子程序,以防有人感兴趣:

sub glob2regex {
    my $glob = shift;

    my $regex = undef;
    my $previousAstrisk = undef;

    foreach my $letter (split(//, $glob)) {
        #
        #    ####Check if previous letter was astrisk
        #
        if ($previousAstrisk) {
            if ($letter eq "*") { #Double astrisk
                $regex .= ".*";
                $previousAstrisk = undef;
                next;
            } else {        #Single astrisk: Write prev match
                $regex .= "[^/]*";
                $previousAstrisk = undef;
            }
        }
        #
        #   ####Quote all Regex characters w/ no meaning in glob
        #
        if ($letter =~ /[\{\}\.\+\(\)\[\]]/) {
            $regex .= "\\$letter";
            #
            #   ####Translate "?" to Regular expression equivelent
            #
        } elsif ($letter eq "?") {
            $regex .= ".";
            #
            #   ####Don't know how to handle astrisks until  the next line
            #
        } elsif ($letter eq "*") {
            $previousAstrisk = 1;
            #
            #   ####Convert backslashes to forward slashes
            #
        } elsif ($letter eq '\\') {
            $regex .= "/";
            #
            #   ####Just a letter
            #
        } else {
            $regex .= $letter;
        }
    }
    #
    #   ####Handle if last letter was astrisk
    #
    if ($previousAstrisk) {
        $regex .= "[^/]*";
    }
    #
    #    ####Globs are anchored to both beginning and ending
    #
    $regex = "^$regex\$";
    return $regex;
}

2 个答案:

答案 0 :(得分:2)

鉴于:

  1. ?只匹配一个字符,除了' /'
  2. *匹配零个或多个字符,但' /'
  3. 除外
  4. **匹配包括/
  5. 在内的任何内容

    如果您不关心格式检查和某些角落情况,例如' ***',那么您可以使用以下策略,首先将特殊字符转换为自定义设计的转义序列,然后将转义序列转换为最终字符串,可能有效:

    my $rgx="^$glob\$";
    $rgx=~ s|!|!e|g;
    $rgx=~ s|[+]|!p|g;
    $rgx=~ s|[*]{2}|!d|g;
    $rgx=~ s|[*]|!s|g;
    $rgx=~ s|[?]|!q|g;
    $rgx=~ s|[.]|\\.|g;
    
    $rgx=~ s|!d|.*|g;
    $rgx=~ s|!s|[^/]*|g;
    $rgx=~ s|!q|[^/]|g;
    $rgx=~ s|!p|\\+|g;
    $rgx=~ s|!e|!|g;
    if ($path =~ m|$rgx|){
        return 1;
    }
    

答案 1 :(得分:1)

显然,从glob中创建正则表达式并没有巧妙的Perl Guru技巧。 Drats。

我能做的最好的事情就是找到像Text::Glob这样的CPAN模块。但是,Text::Glob没有Ant样式扩展的globbing,所以无论如何我都要修改它。而且,代码并不比我已经拥有的代码简单。

所以,我只是坚持我拥有的东西。

非常感谢。