4

正则表达式搜索的目的是从 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/; 

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

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

4

3 回答 3

7

首先,如果你要 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
于 2009-05-04T14:01:12.903 回答
3

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

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

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

于 2009-05-04T13:49:34.553 回答
0

我会做这样的事情,


#!/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";
}

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

于 2009-05-04T16:06:16.207 回答