【问题标题】:How can I get multiple memories from a Perl regex match?如何从 Perl 正则表达式匹配中获得多个记忆?
【发布时间】:2009-05-04 13:27:01
【问题描述】:

正则表达式搜索的目的是从 C++ 头文件中确定所有模板类实例。类实例可以这样格式化:

CMyClass<int> myClassInstance;

CMyClass2<
int,
int
> myClass2Instacen;

通过将整个文件加载到字符串中来执行搜索:

open(FILE, $file);
$string = join('',<FILE>);
close(FILE);

以下正则表达式用于确定类实例,即使类实例跨越字符串中的一行以上:

$search_string = "\s*\w[^typename].*<(\s*\w\s*,?\n?)*)>\s*\w+.*";
$string =~ m/$search_string/; 

问题是即使文件中存在更多类实例,搜索也只会返回一次命中。

是否可以通过使用这种方法从一个正则表达式反向引用变量中获得所有命中?

【问题讨论】:

    标签: regex perl multiline


    【解决方案1】:

    首先,如果你要 slurp 文件,你应该使用File::Slurp。然后你可以这样做:

    my $contents = read_file $file;
    

    read_file 出错时会发出嘶哑的声音。

    其次,[^typename] 不仅排除字符串“typename”,还排除包含任何这些字符的任何字符串。除此之外,对我来说,您使用的模式是否会始终匹配您希望它匹配的东西对我来说并不明显,但我现在无法对此发表评论。

    最后,要一一获取文件中的所有匹配项,在循环中使用 g 修饰符:

    my $source = '3 5 7';
    
    while ( $source =~ /([0-9])/g ) {
        print "$1\n";
    }
    

    现在我有机会查看您的模式,我仍然不确定如何使用 [^typename],但这里有一个示例程序,它捕获尖括号之间的部分(因为这似乎是您在上面捕获的唯一内容):

    use strict;
    use warnings;
    
    use File::Slurp;
    
    my $pattern = qr{
        ^
        \w+                    
        <\s*((?:\w+(?:,\s*)?)+)\s*> 
        \s*
        \w+\s*;
    }mx;
    
    my $source = read_file \*DATA;
    
    while ( $source =~ /$pattern/g ) {
        my $match = $1;
        $match =~ s/\s+/ /g;
        print "$match\n";
    }
    
    __DATA__
    CMyClass<int> myClassInstance;
    
    CMyClass2<
    int,
    int
    > myClass2Instacen;
    
    C:\Temp> t.pl
    int
    int, int
    

    现在,我怀疑您会更喜欢以下内容:

    my $pattern = qr{
        ^
        (
          \w+                    
          <\s*(?:\w+(?:,\s*)?)+\s*> 
          \s*
          \w+
        )
        \s*;
    }mx;
    

    产生:

    C:\Temp> t.pl
    CMyClass<int> myClassInstance
    CMyClass2< int, int > myClass2Instacen
    

    【讨论】:

      【解决方案2】:

      您需要的是\G 修饰符。它在最后一场比赛之后开始你的字符串的下一场比赛。

      这是来自 Perl Doc 的文档(所以链接有问题,所以你必须复制和粘贴):

      http://perldoc.perl.org/perlfaq6.html#What-good-is-'%5cG'-in-a-regular-expression%3f

      【讨论】:

      【解决方案3】:

      我会做这样的事情,

      
      #!/usr/bin/perl -w
      use strict;
      use warnings;
      
      local(*F);
      open(F,$ARGV[0]);
      my $text = do{local($/);};
      my (@hits) = $text =~ m/([a-z]{3})/gsi;
      
      print "@hits\n";
      

      假设您有一些文本文件,例如,

      /home/user$ 更多 a.txt a bb dkl jidij lksj lai suj ldifk kjdfkj bb bb kdjfkal idjksdj fbb kjd fkjd fbb kadfjl bbb bb bb bbd 我

      这将打印出正则表达式中的所有命中:

      
      /home/user$ ./a.pl a.txt
      dkl jid lks lai suj ldi kjd fkj kdj fka idj ksd fbb 
      kjd fkj fbb kad fjl bbb bbd
      


      而针对您的问题的特定解决方案,使用相同的方法,可能看起来像,

      
      #!/usr/bin/perl -w                                                                                                           
      use strict;
      use warnings;
      
      my $text = <<ENDTEXT;
       CMyClass<int> myClassInstance;
      
      CMyClass2<
      int,
      int
      > myClass2Instacen;
      
      
      CMyClass35<
      int,
      int
          > myClass35Instacen;
      
      ENDTEXT
      
      my $basename = "MyClass";
      my (@instances) = $text =~ m/\s*(${basename}[0-9]*\s*\<.*?                                                                
                                  (?=\>\s*${basename})                                                                          
                                  \>\s*${basename}.*?;)/xgsi;
      
      for(my $i=0; $i<@instances; $i++){
          print $i."\t".$instances[$i]."\n\n";
      }
      
      

      当然,您可能需要稍微调整正则表达式以适应数据中的所有边缘情况,但这应该是一个很好的开始。

      【讨论】:

      • 打开我的$fh,$ARGV[0]比local(*F)好;打开(F,$ARGV[0]);在你的例子中使用 Perl::Critic。
      • 我在我的示例中尝试了 Perl::Critic(安装有点麻烦),但它没有为我的示例提供任何 cmets/warnings/errors。另外,我注意到 pre 和 code 块没有正确地转义我的左右尖括号......
      猜你喜欢
      • 2011-08-30
      • 1970-01-01
      • 1970-01-01
      • 2011-02-22
      • 1970-01-01
      • 1970-01-01
      • 2011-03-08
      • 1970-01-01
      相关资源
      最近更新 更多