2012-05-14 44 views
8

Tôi có văn bản như thế này:perl regex để trích xuất các khối multiline

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

Vì vậy, tôi không có một kết thúc khối, chỉ cần một sự khởi đầu khối mới.

Tôi muốn đệ quy có được tất cả các khối:

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

vv

Đoạn mã bên dưới chỉ mang lại cho tôi điều này:

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

Tôi đang làm gì sai?

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); 

Trả lời

2

này nên làm các trick. Bắt đầu từ \ d \ d: \ d \ d được coi là kết thúc chặn.

$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

Parens không chụp của bạn '(? ...)' là dư thừa ở đây, như '(=?. ..) 'cũng có thể sử dụng luân phiên. Ngoài ra, tôi nhận thấy bạn vẫn chưa viết mã tuân thủ nghiêm ngặt, mà trong cuốn sách của tôi là xấu, vì nó khuyến khích thực hành xấu. – TLP

+1

Tôi đã giải thích đầy đủ về ** sử dụng (ing) nghiêm ngặt ** trong chủ đề khác. Bạn có phiền ngừng làm việc này không? – tuxuday

+1

Vâng, tôi quan tâm. Bạn có phiền khi ngừng đăng mã không nghiêm ngặt không? Đây là một môi trường học tập, bạn không mất chi phí * bất cứ thứ gì * để đăng mã khuyến khích thực hành tốt, bây giờ phải không? – TLP

0

Vấn đề của bạn là .*? là không tham lam trong cùng một cách mà .* là tham lam. Khi nó không bị ép buộc, nó khớp với ít nhất có thể, trong trường hợp này là chuỗi rỗng.

Vì vậy, bạn sẽ cần một cái gì đó sau trận đấu không tham lam để cố định ảnh chụp. Tôi đã đưa ra regex này:

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

Như bạn thấy, tôi loại bỏ các tùy chọn /m được một cách chính xác có thể để phù hợp với kết thúc chuỗi trong nhìn về phía trước khẳng định.

Bạn cũng có thể xem xét giải pháp này:

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

Phiên bản 5.10.0 được giới thiệu named capture groups hữu ích cho phù hợp với mẫu không độc hại.

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

Một nhóm chụp được đặt tên. Giống hệt nhau đối với các dấu ngoặc đơn bình thường () nhưng thực tế là nhóm có thể được gọi theo tên trong các cấu trúc biểu thức chính quy khác nhau (chẳng hạn như \g{NAME}) và có thể được truy cập theo tên sau khi kết hợp thành công qua %+ hoặc %-. Xem perlvar để biết thêm chi tiết về các băm %+%-.

Nếu nhiều nhóm chụp riêng biệt có cùng tên thì $+{NAME} sẽ chỉ đến nhóm được xác định ngoài cùng bên trái trong trận đấu.

Các biểu mẫu (?'NAME'pattern)(?<NAME>pattern) là tương đương.

Nhóm chụp được đặt tên cho phép chúng tôi đặt tên các mẫu con trong regex như sau.

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; 

Sử dụng nó như trong

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

Output:

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

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

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

Ví dụ thực sự tuyệt vời của Perl5 hiện đại lại :) – XoR

Các vấn đề liên quan