2008-09-25 33 views
191

Có thể viết cụm từ thông dụng khớp với mẫu lồng nhau xảy ra với số lần không xác định không? Ví dụ, một biểu thức chính quy có thể khớp với dấu ngoặc mở và đóng khi có một số dấu ngoặc mở/đóng không xác định được lồng trong các dấu ngoặc ngoài?Cụm từ thông dụng có thể được sử dụng để khớp với các mẫu lồng nhau không?

Ví dụ:

public MyMethod() 
{ 
    if (test) 
    { 
    // More { } 
    } 

    // More { } 
} // End 

nên phù hợp:

{ 
    if (test) 
    { 
    // More { } 
    } 

    // More { } 
} 
+21

Để trả lời rõ ràng câu hỏi này, trước hết cần phải xác định cụm từ: "biểu thức chính quy". – ridgerunner

+3

@ridgerunner, Richard không cố gắng trả lời câu hỏi, vì vậy có thể nhận xét của bạn được giải quyết tốt hơn cho người nào đó tương tác. – ProfK

+2

Từ sách, * cụm từ thông dụng * không thể làm điều đó, nhưng * biểu thức ngữ cảnh tự do * có thể.Từ các công cụ, các trình phân tích cú pháp biểu thức hiện đại sẽ gọi cụm từ 'regular expression' sử dụng chồng bên ngoài, có nghĩa là có thể quay lại, nghĩa là có thể recurse: đó là' context-free expressions' trong thực tế và như vậy bạn có thể làm điều đó như một một lớp với simili- [PCRE2] (http://www.pcre.org/) (PHP, Java, .NET, Perl, ...) hoặc [ICU] (http: //userguide.icu-project. org/strings/regexp) công cụ -compliant (Obj-C/Swift), thường với cú pháp '(?> ...)', hoặc các lựa chọn thay thế như cú pháp '(? R)' hoặc '(? 0)' . –

Trả lời

235

No. Thật dễ dàng. Một automaton hữu hạn (là cấu trúc dữ liệu bên dưới một biểu thức chính quy) không có bộ nhớ ngoài trạng thái của nó, và nếu bạn có tùy ý làm tổ sâu, bạn cần một automaton tùy ý lớn, va chạm với khái niệm hữu hạn automaton.

Bạn có thể so khớp các phần tử lồng nhau/ghép nối với độ sâu cố định, trong đó độ sâu chỉ bị giới hạn bởi bộ nhớ của bạn, vì máy tự động rất lớn. Tuy nhiên, trong thực tế, bạn nên sử dụng một automaton đẩy xuống, ví dụ như một trình phân tích cú pháp cho ngữ pháp không có ngữ cảnh, ví dụ LL (từ trên xuống) hoặc LR (từ dưới lên). Bạn phải thực hiện hành vi thời gian chạy tồi tệ hơn vào tài khoản: O (n^3) so với O (n), với n = chiều dài (đầu vào).

Có nhiều trình tạo phân tích cú pháp không thể thực hiện được, ví dụ ANTLR cho Java. Tìm một ngữ pháp hiện tại cho Java (hoặc C) cũng không khó.
Đối với nền hơn: Automata Theory tại Wikipedia

+49

Torsten là chính xác như xa như lý thuyết là có liên quan. Trong thực tế, nhiều triển khai có một số mẹo để cho phép bạn thực hiện "biểu thức chính quy" đệ quy. Ví dụ. xem chương "Các mẫu đệ quy" trong http://php.net/manual/en/regexp.reference.php – daremon

+2

Tôi bị hư hỏng bởi sự dạy dỗ của tôi trong Xử lý ngôn ngữ tự nhiên và lý thuyết automata nó bao gồm. –

+4

Câu trả lời rõ ràng. Tốt nhất "tại sao không" tôi từng thấy. –

-3

No. Bạn cần trình phân tích cú pháp toàn diện cho loại sự cố này.

+9

... hoặc Perl5.10 hoặc cao hơn –

31

lẽ làm việc giải pháp Perl, nếu chuỗi là trên cùng một dòng:

my $NesteD ; 
$NesteD = qr/ \{([^{}] | (??{ $NesteD }))* \} /x ; 

if ($Stringy =~ m/\b(\w+$NesteD)/x) { 
    print "Found: $1\n" ; 
    } 

HTH

EDIT: kiểm tra:

Và một điều nữa bởi Torsten Marek (người đã chỉ ra một cách chính xác, rằng nó không phải là regex nữa):

+9

Yup. Perl của "biểu thức chính quy" không (và đã không được cho một thời gian rất dài). Cần lưu ý rằng regexes đệ quy là một tính năng mới trong Perl 5.10 và mặc dù bạn có thể làm điều này có thể bạn không nên ở hầu hết các trường hợp thường xuất hiện (ví dụ: phân tích cú pháp HTML). –

+0

http://perldoc.perl.org/perlretut.html –

14

Pumping lemma for regular languages là lý do tại sao bạn không thể làm điều đó.

Automaton được tạo sẽ có số trạng thái hữu hạn, ví dụ k, vì vậy một chuỗi dấu ngoặc mở k + 1 bị ràng buộc có trạng thái lặp lại ở đâu đó (khi quá trình tự động hóa các ký tự). Một phần của chuỗi giữa cùng một trạng thái có thể được nhân đôi vô số lần và automaton sẽ không biết sự khác biệt.

Cụ thể, nếu nó chấp nhận dấu ngoặc mở k + 1 theo sau là dấu ngoặc k + 1 (mà nó cần), nó cũng sẽ chấp nhận số niềng răng đang mở theo sau là dấu ngoặc đóng k + 1 không thay đổi (mà nó không nên t).

12

Cụm từ thông dụng phù hợp sẽ không thể thực hiện được khi bạn rời khỏi lĩnh vực Ngôn ngữ thông thường để tiếp cận các lãnh thổ trong Ngữ cảnh tự do.

Tuy nhiên, các gói "cụm từ thông dụng" mà nhiều ngôn ngữ cung cấp sẽ mạnh hơn rất nhiều. Ví dụ: Lua cụm từ thông dụng có ký hiệu "%b()" sẽ khớp với dấu ngoặc đơn cân bằng. Trong trường hợp của bạn, bạn sẽ sử dụng "%b{}"

Một công cụ tinh vi khác tương tự như sed là gema, nơi bạn sẽ khớp các dấu ngoặc nhọn cân bằng rất dễ dàng với {#}.

Vì vậy, tùy thuộc vào các công cụ bạn có theo ý của mình, "cụm từ thông dụng" của bạn (theo nghĩa rộng hơn) có thể khớp với dấu ngoặc đơn lồng nhau.

3

như zsolt đã đề cập, một số động cơ regex hỗ trợ đệ quy - tất nhiên, đây thường là những người sử dụng thuật toán backtracking để nó sẽ không đặc biệt hiệu quả. ví dụ: /(?>[^{}]*){(?>[^{}]*)(?R)*(?>[^{}]*)}/sm

18

Có, nếu có .NET RegEx-engine. .Net engine hỗ trợ máy trạng thái hữu hạn được cung cấp với một chồng bên ngoài. thấy details

+8

Như những người khác đã đề cập, .NET là _not_ chỉ có động cơ regex có khả năng thực hiện việc này. –

0

Điều này dường như làm việc: /(\{(?:\{.*\}|[^\{])*\})/m

+1

Nó cũng có vẻ phù hợp với '{{}' mà nó không nên –

27

Sử dụng biểu thức thông thường để kiểm tra các mô hình lồng nhau là rất dễ dàng.

'/(\((?>[^()]+|(?1))*\))/' 
+2

Tôi đồng ý. Tuy nhiên, một vấn đề với cú pháp nhóm nguyên tử '(?> ...)' (trong PHP 5.2) là phần '?>' Được hiểu là: "end-of-script"! Đây là cách tôi sẽ viết nó: '/ \ ((?: [^()] ++ | (? R)) * + \) /'. Điều này hiệu quả hơn một chút cho cả kết hợp và không khớp. Ở dạng tối thiểu của nó, '/ \ (([^()] | (? R)) * \) /', nó thực sự là một điều đẹp! – ridgerunner

+1

Double +? Tôi đã sử dụng '(? 1)' để cho phép nhận xét nằm trong văn bản khác (tôi trích xuất nó và đơn giản hóa nó từ biểu thức chính quy của địa chỉ email của tôi). Và '(?>' Được sử dụng bởi vì tôi tin rằng nó làm cho nó thất bại nhanh hơn (nếu cần). Điều đó có đúng không? – MichaelRushton

+5

Bạn có thể thêm một lời giải thích cho từng phần của regex không? – Dwayne

5

Sử dụng kết hợp đệ quy trong công cụ regex PHP nhanh hơn so với kết hợp thủ tục của dấu ngoặc đơn. đặc biệt là với các chuỗi dài hơn.

http://php.net/manual/en/regexp.reference.recursive.php

ví dụ:

$patt = '!\((?: (?: (?>[^()]+) | (?R))*) \)!x'; 

preg_match_all($patt, $str, $m); 

vs

matchBrackets($str); 

function matchBrackets ($str, $offset = 0) { 

    $matches = array(); 

    list($opener, $closer) = array('(', ')'); 

    // Return early if there's no match 
    if (false === ($first_offset = strpos($str, $opener, $offset))) { 
     return $matches; 
    } 

    // Step through the string one character at a time storing offsets 
    $paren_score = -1; 
    $inside_paren = false; 
    $match_start = 0; 
    $offsets = array(); 

    for ($index = $first_offset; $index < strlen($str); $index++) { 
     $char = $str[ $index ]; 

     if ($opener === $char) { 
      if (! $inside_paren) { 
       $paren_score = 1; 
       $match_start = $index; 
      } 
      else { 
       $paren_score++; 
      } 
      $inside_paren = true; 
     } 
     elseif ($closer === $char) { 
      $paren_score--; 
     } 

     if (0 === $paren_score) { 
      $inside_paren = false; 
      $paren_score = -1; 
      $offsets[] = array($match_start, $index + 1); 
     } 
    } 

    while ($offset = array_shift($offsets)) { 

     list($start, $finish) = $offset; 

     $match = substr($str, $start, $finish - $start); 
     $matches[] = $match; 
    } 

    return $matches; 
} 
0

My question+answer có liên quan và tôi làm cho một biểu thức và meta-biểu thức có thể phù hợp với tùy ý cấp (hữu hạn) của tổ. Nó khá là thú vị nhưng bạn có thể mong đợi điều gì khác? Sử dụng backreferences trong trận đấu nếu động cơ của bạn hỗ trợ nó.

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