2012-05-14 12 views
8

mam tekst tak:Perl regex do wydobywania bloków multilinii

00:00 stuff 
00:01 more stuff 
multi line 
    and going 
00:02 still 
    have 

Więc nie masz końcu bloku, tylko nowy początek bloku.

chcę rekursywnie uzyskać wszystkie bloki:

1 = 00:00 stuff 
2 = 00:01 more stuff 
multi line 
    and going 

itp

Kod poniżej tylko daje mi to:

$VAR1 = '00:00'; 
$VAR2 = ''; 
$VAR3 = '00:01'; 
$VAR4 = ''; 
$VAR5 = '00:02'; 
$VAR6 = ''; 

Co robię źle?

my $text = '00:00 stuff 
00:01 more stuff 
multi line 
and going 
00:02 still 
have 
    '; 
my @array = $text =~ m/^([0-9]{2}:[0-9]{2})(.*?)/gms; 
print Dumper(@array); 

Odpowiedz

2

To powinno załatwić sprawę. Rozpoczęcie następnego \ d \ d: \ d \ d traktowane jest jako koniec bloku.

$Str = '00:00 stuff 
00:01 more stuff 
multi line 
    and going 
00:02 still 
    have 
00:03 still 
    have' ; 

@Blocks = ($Str =~ m#(\d\d:\d\d.+?(?:(?=\d\d:\d\d)|$))#gs); 

print join "--\n", @Blocks; 
+1

organizacji non-przechwytywanie parens '(?: ...)' są tu zbędne, jako '(=?. ..) 'może również używać alternacji. Zauważam też, że nadal nie piszesz "ścisłego" kodu zgodnego z zasadami, co w mojej książce jest złe, ponieważ zachęca do złych praktyk. – TLP

+1

Podałem wystarczające wyjaśnienie na temat ** użycia (ing) strict ** w drugim wątku. Czy możesz przestać to robić? – tuxuday

+1

Tak, mam coś przeciwko. Czy mógłbyś przestać publikować nieostry kod? To jest środowisko do nauki, nie kosztuje * niczego *, aby opublikować kod, który zachęca do dobrej praktyki, teraz to robi? – TLP

0

Twój problem polega na tym .*? nie jest chciwa w taki sam sposób, .* jest chciwy. Kiedy nie jest wymuszona, dopasowuje się tak mało, jak to tylko możliwe, co w tym przypadku jest pustym łańcuchem.

Potrzebne będzie coś po nieuczciwym dopasowaniu, aby zakotwiczyć przechwytywanie. Wpadłem na ten regex:

my @array = $text =~ m/\n?([0-9]{2}:[0-9]{2}.*?)(?=\n[0-9]{2}:|$)/gs; 

Jak widać, usunąłem opcja /m dokładnie móc dopasować koniec sznurka w twierdzeniu, spojrzenie na wprost.

Można również rozważyć takie rozwiązanie:

my @array = split /(?=[0-9]{2}:[0-9]{2})/, $text; 
4

Wersja 5.10.0 wprowadziła named capture groups, które są przydatne do porównywania nietrywialnych wzorów.

(?'NAME'pattern)
(?<NAME>pattern)

Nazwana grupa przechwytywania. Identyczny pod każdym względem zwykłych nawiasach przechwytywania () ale za dodatkową fakt, że grupa może mieć określoną nazwą w różnych regularnych konstruktów ekspresyjnych (takich jak \g{NAME}) i mogą być dostępne przez imię po udanym meczu poprzez %+ lub %-. Więcej informacji na temat skrótów %+ i %- można znaleźć w sekcji perlvar.

Jeśli wiele odrębnych grup przechwytywania ma tę samą nazwę, wówczas $+{NAME} będzie odnosić się do lewej grupy zdefiniowanej w dopasowaniu.

Formularze (?'NAME'pattern) i (?<NAME>pattern) są równoważne.

Nazwane grupy przechwytywania umożliwiają nam nazywanie podtekstów w regex jak poniżej.

use 5.10.0; # named capture buffers 

my $block_pattern = qr/ 
    (?<time>(?&_time)) (?&_sp) (?<desc>(?&_desc)) 

    (?(DEFINE) 
    # timestamp at logical beginning-of-line 
    (?<_time> (?m:^) [0-9][0-9]:[0-9][0-9]) 

    # runs of spaces or tabs 
    (?<_sp> [ \t]+) 

    # description is everything through the end of the record 
    (?<_desc> 
     # s switch makes . match newline too 
     (?s: .+?) 

     # terminate before optional whitespace (which we remove) followed 
     # by either end-of-string or the start of another block 
     (?= (?&_sp)? (?: $ | (?&_time))) 
    ) 
) 
/x; 

Używaj go jak w

my $text = '00:00 stuff 
00:01 more stuff 
multi line 
and going 
00:02 still 
have 
    '; 

while ($text =~ /$block_pattern/g) { 
    print "time=[$+{time}]\n", 
     "desc=[[[\n", 
     $+{desc}, 
     "]]]\n\n"; 
} 

Wyjście:

$ ./blocks-demo 
time=[00:00] 
desc=[[[ 
stuff 
]]] 

time=[00:01] 
desc=[[[ 
more stuff 
multi line 
and going 
]]] 

time=[00:02] 
desc=[[[ 
still 
have 
]]]
+1

Naprawdę świetnym przykładem nowoczesnego Perl5 ponownie :) – XoR

Powiązane problemy