2010-08-24 20 views
7

Làm cách nào tôi có thể xóa việc chụp từ các nhóm phụ lồng nhau tùy ý trong chuỗi regex Perl? Tôi muốn lồng ghép bất kỳ regex nào vào một biểu thức bao bọc để nắm bắt sub-regex như một thực thể toàn bộ cũng như các nhóm tiếp theo được biết đến tĩnh. Tôi có cần phải chuyển đổi chuỗi regex theo cách thủ công vào việc sử dụng tất cả các nhóm không bắt giữ (?:) (và hy vọng tôi không gây rối), hoặc có cơ chế thư viện Perl hoặc thư viện cung cấp điều này không?Làm cách nào để tạo một regex Perl tùy ý hoàn toàn không bắt được? (Trả lời: Bạn không thể)

# How do I 'flatten' $regex to protect $2 and $3? 
# Searching 'ABCfooDE' for 'foo' OK, but '((B|(C))fo(o)?(?:D|d)?)', etc., breaks. 
# I.E., how would I turn it effectively into '(?:(?:B|(?:C))fo(?:o)?(?:D|d)?)'? 
sub check { 
    my($line, $regex) = @_; 
    if ($line =~ /(^.*)($regex)(.*$)/) { 
    print "<", $1, "><", $2, "><", $3, ">\n"; 
    } 
} 

Phụ Lục: Tôi mơ hồ nhận thức $&, $`$' và đã được khuyên để tránh chúng nếu có thể, và tôi không có quyền truy cập vào ${^PREMATCH}, ${^MATCH}${^POSTMATCH} trong môi trường Perl 5,8 của tôi . Ví dụ trên có thể được phân đoạn thành các đoạn 2/3 bằng cách sử dụng các phương thức như thế này và các trường hợp thực tế phức tạp hơn có thể lặp lại thủ công điều này, nhưng tôi nghĩ tôi muốn một giải pháp chung nếu có thể.

Câu trả lời được chấp nhận: Điều tôi muốn tồn tại và đáng ngạc nhiên (ít nhất là) không phải là một nhóm đóng gói làm cho nội dung của nó mờ đục, chẳng hạn như backreferences vị trí sau đó xem nội dung dưới dạng một thực thể duy nhất và tên de-scoped. gbacon có cách giải quyết hữu ích cho Perl 5.10+ và FM hiển thị cơ chế lặp lại thủ công cho bất kỳ phiên bản nào có thể thực hiện cùng một hiệu ứng trong các trường hợp cụ thể, nhưng j_random_hacker gọi nó là không có cơ chế ngôn ngữ thực để đóng gói các biểu thức con.

+1

+1 Công việc tuyệt vời với các chỉnh sửa tiếp theo. – FMc

Trả lời

8

Nói chung, bạn không thể.

Thậm chí nếu bạn có thể chuyển đổi tất cả các (...) s vào (?:...) s, điều này sẽ không làm việc trong trường hợp tổng quát vì mô hình có thể yêu cầu backreferences: ví dụ: /(.)X\1/, khớp với bất kỳ ký tự nào, theo sau là X, theo sau là ký tự được so khớp ban đầu.

Vì vậy, không có cơ chế Perl để loại bỏ kết quả bị bắt "sau khi thực tế", không có cách nào để giải quyết vấn đề của bạn cho tất cả các regex. Tốt nhất bạn có thể làm (hoặc có thể làm nếu bạn có Perl 5.10) là sử dụng gbacon's suggestion và hy vọng sẽ tạo ra một tên duy nhất cho bộ đệm chụp.

0

này không vô hiệu hóa bắt, nhưng có thể thực hiện những gì bạn muốn:

$ perl -wle 'my $_ = "123abc"; /(\d+)/ && print "num: $1"; { /([a-z]+)/ && print "letter: $1"; } print "num: $1";' 
num: 123 
letter: abc 
num: 123 

Bạn tạo một phạm vi mới và $ 1 bên ngoài nó sẽ không bị ảnh hưởng.

7

Một cách để bảo vệ subpatterns mà bạn quan tâm là sử dụng named capture buffers:

Bên cạnh đó, tính đến Perl 5.10.0 bạn có thể sử dụng bộ đệm chụp được đặt tên và tên backreferences. Ký hiệu là (?<name>...) để khai báo và \k<name> để tham chiếu. Bạn cũng có thể sử dụng dấu nháy đơn thay vì dấu ngoặc nhọn để phân định tên; và bạn có thể sử dụng cú pháp backreference \g{name} có ngoặc vuông. Có thể tham khảo bộ đệm chụp có tên theo số tuyệt đối và tương đối. Bên ngoài mẫu, bộ đệm chụp được đặt tên có sẵn thông qua hàm băm %+. Khi các bộ đệm khác nhau trong cùng một mẫu có cùng tên, $+{name}\k<name> tham chiếu đến nhóm được xác định ngoài cùng bên trái.

Trong bối cảnh của câu hỏi của bạn, check trở thành

sub check { 
    use 5.10.0; 
    my($line, $regex) = @_; 
    if ($line =~ /(^.*)($regex)(.*$)/) { 
    print "<", $+{one}, "><", $+{two}, "><", $+{three}, ">\n"; 
    } 
} 

Sau đó, gọi nó với

my $pat = qr/(?<one>(?<two>B|(?<three>C))fo(o)?(?:D|d)?)/; 
check "ABCfooDE", $pat; 

đầu ra

<CfooD><C><C>
+0

Đây là một kỹ thuật gọn gàng mà tôi đã không nhận thức được, nhưng không may, tôi bị mắc kẹt trong một môi trường RHEL 4 (Perl v5.8.5), vì vậy tôi không thể sử dụng nó trong thời gian này. – Jeff

5

này không giải quyết các trường hợp chung, nhưng ví dụ cụ thể của bạn có thể được xử lý với /g tùy chọn trong bối cảnh vô hướng, mà sẽ cho phép bạn chia vấn đề thành hai trận đấu, thứ hai bắt đầu từ nơi đầu tiên rời đi:

sub check { 
    my($line, $regex) = @_; 
    my ($left_side, $regex_match) = ($1, $2) if $line =~ /(^.*)($regex)/g; 
    my $right_side = $1 if $line =~ /(.*$)/g; 
    print "<$left_side> <$regex_match> <$right_side>\n"; # <AB> <CfooD> <E123> 
} 

check('ABCfooDE123', qr/((B|(C))fo(o)?(?:D|d)?)/); 
+0

Cảm ơn, kỹ thuật này có thể đủ tốt để tôi sử dụng cho các trường hợp sử dụng thực tế của tôi bây giờ. Tôi nghĩ rằng cuối cùng tôi sẽ cần một giải pháp tổng quát hơn, vì vậy tôi sẽ tiếp tục mở câu hỏi. – Jeff

2

Nếu tất cả bạn cần là một phần của chuỗi trước và sau trận đấu, bạn có thể sử dụng @-@+ mảng để có được hiệu số vào chuỗi lần xuất hiện:

sub check { 
    my ($line, $regex) = @_; 
    if ($line =~ /$regex/) { 
     my $pre = substr $line, 0, $-[0]; 
     my $match = substr $line, $-[0], $+[0] - $-[0]; 
     my $post = substr $line, $+[0]; 
     print "<$pre><$match><$post>\n"; 
    } 
} 
1

Perl của phiên bản> 5.22 là được báo cáo có công cụ sửa đổi '/ n', tất cả sẽ tắt tất cả.

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