Как я могу получить несколько воспоминаний о матче с Perl? - PullRequest
4 голосов
/ 04 мая 2009

Цель поиска в регулярном выражении - определить все экземпляры класса шаблона из заголовочных файлов 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/; 

Проблема в том, что поиск возвращает только одно попадание, даже если в файлах существует больше экземпляров классов.

Можно ли получить все попадания с помощью этого подхода из одной из переменных обратного обращения к регулярному выражению?

Ответы [ 3 ]

7 голосов
/ 04 мая 2009

Во-первых, если вы собираетесь использовать файлы, вы должны использовать 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
3 голосов
/ 04 мая 2009

Вам нужен модификатор \G. Он начинает следующее совпадение вашей строки после последнего совпадения.

Вот документация от Perl Doc (у SO проблемы со ссылкой, поэтому вам придется скопировать и вставить):

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

0 голосов
/ 04 мая 2009

Я бы сделал что-то подобное,


#!/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$ more a.txt
a bb dkl jidij lksj lai suj ldifk kjdfkj bb
bb kdjfkal idjksdj fbb kjd fkjd fbb  kadfjl bbb
bb bb bbd i

это распечатает все хиты из регулярного выражения:


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

конечно, вам, вероятно, придется немного изменить регулярное выражение, чтобы оно соответствовало всем крайним случаям в ваших данных, но это должно быть довольно хорошее начало.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...