2009-08-21 26 views
54

Có hành vi được xác định nào về cách biểu thức chính quy nên xử lý hành vi lưu giữ của các dấu ngoặc đơn lồng nhau không? Cụ thể hơn, bạn có thể kỳ vọng hợp lý rằng các công cụ khác nhau sẽ nắm bắt các dấu ngoặc đơn bên ngoài ở vị trí đầu tiên và các dấu ngoặc đơn lồng nhau ở các vị trí tiếp theo không?Các nhóm chụp lồng nhau được đánh số như thế nào trong các biểu thức chính quy?

Hãy xem xét các mã PHP sau (sử dụng PCRE biểu thức thông thường)

<?php 
    $test_string = 'I want to test sub patterns'; 
    preg_match('{(I (want) (to) test) sub (patterns)}', $test_string, $matches); 
    print_r($matches); 
?> 

Array 
(
    [0] => I want to test sub patterns //entire pattern 
    [1] => I want to test   //entire outer parenthesis 
    [2] => want    //first inner 
    [3] => to    //second inner 
    [4] => patterns    //next parentheses set 
) 

Toàn bộ biểu hiện trong ngoặc đơn được chụp đầu tiên (Tôi muốn thử nghiệm), và sau đó là mô hình trong ngoặc bên trong được chụp tiếp theo ("muốn" và "đến"). Điều này làm cho cảm giác hợp lý, nhưng tôi có thể thấy một trường hợp hợp lý không kém được thực hiện để lần đầu tiên nắm bắt các dấu ngoặc đơn phụ, và THEN chụp toàn bộ mẫu. Vì vậy, đây là "nắm bắt toàn bộ điều đầu tiên" hành vi được xác định trong công cụ biểu thức chính quy, hoặc là nó sẽ phụ thuộc vào bối cảnh của mô hình và/hoặc hành vi của động cơ (PCRE khác với C# là khác nhau so với Java khác với v.v.)?

+0

Nếu bạn thực sự quan tâm đến tất cả các hương vị regex, "ngôn ngữ thuyết bất khả tri "tag là những gì bạn muốn. Có quá nhiều hương vị để liệt kê tất cả, và hầu hết trong số họ không phù hợp với bất kỳ tiêu chuẩn thực sự (al mặc dù chúng khá nhất quán khi nói đến việc đánh số nhóm). –

+0

Nhóm có thể được truy cập bằng $ 1, $ 2, $ 3 .... v.v. Làm cách nào để truy cập nhóm thứ 10? Nó sẽ là $ 10? Tôi không nghĩ rằng $ 10 sẽ làm việc vì nó sẽ được hiểu là $ 1 theo sau là 0. Điều này có nghĩa là chúng tôi chỉ có thể có tối đa 9 nhóm không? Nếu tác giả có thể, xin vui lòng, bao gồm điều này như là một phần của câu hỏi thì đây sẽ là nơi duy nhất để biết tất cả về các nhóm lồng nhau trong các biểu thức chính quy. – LionHeart

Trả lời

44

Từ perlrequick

Nếu nhóm trong một regex là lồng nhau, $ 1 được nhóm với mở ngoặc tận cùng bên trái, $ 2 tiếp theo mở ngoặc vv

Cập nhật

tôi không sử dụng PCRE nhiều, như tôi thường sử dụng thật;), nhưng PCRE's docs hiển thị giống như Perl:

SUBPATTERNS

2. Nó thiết lập các subpattern như là một subpattern capturing. Điều này có nghĩa là, khi toàn bộ mẫu khớp với nhau, phần của chuỗi chủ đề khớp với mẫu con được truyền lại cho người gọi qua đối số ovector của pcre_exec(). Mở ngoặc đơn được tính từ trái sang phải (bắt đầu từ 1) để lấy số cho các mẫu con chụp.

Ví dụ, nếu chuỗi "nhà vua đỏ" là phù hợp với mô hình

the ((red|white) (king|queen)) 

các chuỗi con bắt được "vua đỏ", "đỏ", và "vua", và được đánh số 1, 2, và 3, tương ứng.

Nếu PCRE đang trôi khỏi tương thích regex Perl, có lẽ từ viết tắt phải được định nghĩa lại - "Perl Cognate Regular Expressions", "Perl Comparable Regular Expressions" hoặc một cái gì đó. Hoặc chỉ cần xóa bỏ các chữ cái của ý nghĩa.

+0

+1 nhưng lưu ý rằng anh ấy không sử dụng Perl. –

+1

@Sinan: anh ấy đang sử dụng PCRE trong PHP, đó là "Cụm từ thông dụng tương thích Perl"; Vì vậy, nó nên được khá giống như sử dụng Perl trực tiếp –

+2

Pascal, PCRE bắt đầu như một nỗ lực để trở thành một Perl tương thích Regular Expression thiết lập, nhưng trong những năm gần đây hai đã phân kỳ một chút. Vẫn rất giống nhau, nhưng có những khác biệt tinh tế trong các bộ tính năng nâng cao. (Ngoài ra, theo câu hỏi, tôi quan tâm đến tất cả các nền tảng) –

4

Trình tự chụp theo thứ tự của paren trái là tiêu chuẩn trên tất cả các nền tảng tôi đã làm việc ở. (Perl, php, ruby, egrep)

+0

"chụp theo thứ tự của dấu ngoặc đơn bên trái" Cảm ơn vì điều đó, đó là một cách ngắn gọn hơn để mô tả hành vi. –

+2

Bạn đã làm việc trên nền tảng nào? –

+1

Bạn có thể ghi lại số lần chụp trong Perl 5.10 và Perl 6. –

8

Mỗi hương vị regex tôi biết số nhóm theo thứ tự trong đó dấu ngoặc đơn mở xuất hiện. Các nhóm bên ngoài được đánh số trước các nhóm con của chúng chỉ là một kết quả tự nhiên, chứ không phải chính sách rõ ràng.

Trường hợp thú vị là với các nhóm được đặt tên. Trong hầu hết các trường hợp, chúng tuân theo cùng một chính sách đánh số bởi các vị trí tương đối của các parens - tên này chỉ là một bí danh cho số đó. Tuy nhiên, trong .NET regexes các nhóm được đặt tên được đánh số riêng biệt với các nhóm được đánh số. Ví dụ:

Regex.Replace(@"one two three four", 
       @"(?<one>\w+) (\w+) (?<three>\w+) (\w+)", 
       @"$1 $2 $3 $4") 

// result: "two four one three" 

Trên thực tế, số là một bí danh cho tên; các số được chỉ định cho các nhóm được đặt tên bắt đầu từ nơi các nhóm được đánh số "thực". Điều đó có vẻ giống như một chính sách kỳ lạ, nhưng có một lý do chính đáng cho nó: trong .NET regexes bạn có thể sử dụng cùng một tên nhóm nhiều hơn một lần trong một regex. Điều đó làm cho regexes thể như một từ this thread cho phù hợp với số dấu chấm động từ địa phương khác nhau:

^[+-]?[0-9]{1,3} 
(?: 
    (?:(?<thousand>\,)[0-9]{3})* 
    (?:(?<decimal>\.)[0-9]{2})? 
| 
    (?:(?<thousand>\.)[0-9]{3})* 
    (?:(?<decimal>\,)[0-9]{2})? 
| 
    [0-9]* 
    (?:(?<decimal>[\.\,])[0-9]{2})? 
)$ 

Nếu có một phân cách hàng ngàn, nó sẽ được lưu lại trong nhóm "ngàn" không có vấn đề mà một phần của regex phù hợp nó . Tương tự, dấu tách thập phân (nếu có) sẽ luôn được lưu trong nhóm "thập phân". Tất nhiên, có nhiều cách để xác định và trích xuất các dấu tách mà không có các nhóm có thể tái sử dụng được đặt tên, nhưng cách này thuận tiện hơn nhiều, tôi nghĩ nó nhiều hơn là biện minh cho lược đồ đánh số lạ.

Và sau đó có Perl 5.10+, điều này cho phép chúng tôi kiểm soát nhiều hơn đối với việc chụp nhóm hơn là tôi biết phải làm gì với. : D

14

Yeah, đây là tất cả khá nhiều được xác định rõ cho tất cả các ngôn ngữ bạn quan tâm:

  • Java-http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html#cg
    "nhóm Chụp được đánh số bằng cách đếm ngoặc mở từ bên trái sang phải. ... Nhóm 0 luôn đứng cho toàn bộ biểu thức. "
  • Net-http://msdn.microsoft.com/en-us/library/bs2twtah(VS.71).aspx
    "chụp sử dụng() được đánh số tự động dựa trên thứ tự của các dấu mở ngoặc, bắt đầu từ một Người chụp đầu tiên, chụp yếu tố số không, là văn bản phù hợp bởi các biểu thức chính quy toàn bộ. mô hình ")
  • PHP (PCRE chức năng)-http://www.php.net/manual/en/function.preg-replace.php#function.preg-replace.parameters
    ".. \ 0 hoặc $ 0 nghĩa là nội dung phù hợp bởi toàn bộ mô hình ngoặc mở cửa được tính từ trái sang phải (bắt đầu từ 1) để có được số lượng của mô hình con chụp. " (Nó cũng là sự thật trong những chức năng POSIX bị phản đối)
  • PCRE-http://www.pcre.org/pcre.txt
    Để thêm vào những gì Alan M nói, tìm kiếm cho "chuỗi con thế nào pcre_exec() trả về bắt" và đọc đoạn thứ năm rằng sau:

    khác nhau
     
    The first pair of integers, ovector[0] and ovector[1], identify the 
    portion of the subject string matched by the entire pattern. The next 
    pair is used for the first capturing subpattern, and so on. The value 
    returned by pcre_exec() is one more than the highest numbered pair that 
    has been set. For example, if two substrings have been captured, the 
    returned value is 3. If there are no capturing subpatterns, the return 
    value from a successful match is 1, indicating that just the first pair 
    of offsets has been set. 
    
  • Perl - http://perldoc.perl.org/perlre.html#Capture-buffers
    $ 1, $ 2 vv trận đấu nhóm như bạn mong muốn chụp (tức là bằng cách xuất hiện của các mở khung), tuy nhiên $ 0 trả về tên chương trình, không phải toàn bộ chuỗi truy vấn - để nhận được rằng bạn chúng tôi Thay vào đó, e $ &.

Bạn sẽ tìm thấy nhiều kết quả tương tự cho các ngôn ngữ khác (Python, Ruby và các ngôn ngữ khác).

Bạn nói rằng không kém phần hợp lý khi liệt kê các nhóm chụp bên trong trước tiên và bạn nói đúng - đó chỉ là vấn đề lập chỉ mục khi đóng, thay vì mở, mở. (nếu tôi hiểu bạn một cách chính xác). Làm điều này ít tự nhiên hơn (ví dụ nó không tuân theo quy ước hướng đọc) và do đó làm cho nó khó hơn (có lẽ không đáng kể) để xác định, bằng cách kiểm tra, nhóm chụp sẽ ở một chỉ số kết quả nhất định.

Đặt toàn bộ chuỗi đối sánh ở vị trí 0 cũng có ý nghĩa - chủ yếu là để nhất quán. Nó cho phép toàn bộ chuỗi kết hợp duy trì ở cùng một chỉ số bất kể số lượng nhóm thu thập từ regex đến regex và bất kể số lượng nhóm chụp thực sự khớp với bất kỳ thứ gì (ví dụ Java sẽ thu hẹp độ dài của mảng nhóm phù hợp cho mỗi lần chụp nhóm không phù hợp với bất kỳ nội dung nào (ví dụ như "a (. *) pattern") Bạn luôn có thể kiểm tra capturing_group_results [capturing_group_results_length - 2], nhưng nó không dịch tốt sang ngôn ngữ để Perl tự động tạo biến ($ 1 , $ 2 vv) (Perl là một ví dụ xấu của khóa học, vì nó sử dụng $ & cho biểu thức phù hợp, nhưng bạn nhận được ý tưởng :)

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