2013-08-09 24 views
5

Tôi có một biểu thức chính quy có dạngCó cách nào để xác định các viết tắt tùy chỉnh trong các biểu thức chính quy không?

def parse(self, format_string): 
    for m in re.finditer(
     r"""(?: \$ \(([^)]+) \)) # the field access specifier 
      | (
       (?: 
        \n | . (?= \$ \() # any one single character before the '$(' 
       ) 
       | (?: 
        \n | . (?! \$ \() # any one single character, except the one before the '$(' 
       )* 
      )""", 
     format_string, 
     re.VERBOSE): 
    ... 

Tôi muốn thay thế tất cả các chuỗi lặp đi lặp lại (\$ \() với một số viết tắt tùy chỉnh "liên tục", mà sẽ trông như thế này:

def parse(self, format_string): 
    re.<something>('\BEGIN = \$\(') 
    for m in re.finditer(
     r"""(?: \BEGIN ([^)]+) \)) # the field access specifier 
      | (
       (?: 
        \n | . (?= \BEGIN) # any one single character before the '$(' 
       ) 
       | (?: 
        \n | . (?! \BEGIN) # any one single character, except the one before the '$(' 
       )* 
      )""", 
     format_string, 
     re.VERBOSE): 
    ... 

Có cách nào để thực hiện điều này với chính biểu thức thông thường (nghĩa là không sử dụng định dạng chuỗi của Python để thay thế \BEGIN bằng \$\()?

Làm rõ: nguồn Python hoàn toàn là ngữ cảnh và minh hoạ. Tôi đang tìm giải pháp RE, mà sẽ có sẵn trong một số phương ngữ RE (có thể không phải là Python), không phải là giải pháp đặc biệt cho Python.

+0

Không, xin lỗi. Có gì sai với định dạng chuỗi? –

+0

Vâng, không phải "di động" theo nghĩa là biểu thức chính quy kết quả có viết tắt tùy chỉnh có thể được sử dụng trong các ngôn ngữ lập trình khác nhau. –

+1

Nhiều phương ngữ regex không hỗ trợ regexes tiết. Hoặc xác nhận. –

Trả lời

9

Tôi không nghĩ rằng điều này là có thể trong hương vị regex của Python. Bạn sẽ cần đệ quy (hoặc sử dụng lại mẫu) mà chỉ được PCRE hỗ trợ. Trong thực tế, PCRE thậm chí còn đề cập đến cách định nghĩa các viết tắt hoạt động trong số man page của nó (tìm kiếm "Định nghĩa các mẫu con").

Trong PCRE, bạn có thể sử dụng cú pháp đệ quy theo cách tương tự như backreferences - ngoại trừ việc mẫu được áp dụng một lần nữa, thay vì cố gắng lấy cùng một văn bản như từ một backreference. Ví dụ:

/(\d\d)-(?1)-(?1)/ 

Trận một cái gì đó giống như một ngày (nơi (?1) sẽ được thay thế bằng với \d\d và đánh giá một lần nữa). Điều này thực sự mạnh mẽ, bởi vì nếu bạn sử dụng cấu trúc này bên trong nhóm được tham chiếu, bạn sẽ nhận được đệ quy - nhưng chúng ta thậm chí không cần nó ở đây. Trên đây cũng làm việc với các nhóm tên là:

/(?<my>\d\d)-(?&my)-(?&my)/ 

Bây giờ chúng tôi đã thực sự chặt chẽ, nhưng định nghĩa cũng là việc sử dụng đầu tiên của mô hình, trong đó có phần clutters lên sự biểu hiện. Bí quyết là sử dụng mẫu đầu tiên ở một vị trí không bao giờ được đánh giá. Các trang người đàn ông đề nghị một điều kiện đó là phụ thuộc vào một (không tồn tại) nhóm DEFINE:

/ 
(?(DEFINE) 
    (?<my>\d\d) 
) 
(?&my)-(?&my)-(?&my) 
/x 

các cấu trúc (?(group)true|false) áp dụng mô hình true nếu nhóm group đã được sử dụng trước đó, và (tùy chọn) mẫu false khác. Vì không có nhóm DEFINE, điều kiện sẽ luôn sai và mẫu true sẽ bị bỏ qua. Do đó, chúng tôi có thể đặt tất cả các loại định nghĩa ở đó, mà không sợ rằng chúng sẽ được áp dụng và làm hỏng kết quả của chúng tôi. Bằng cách này, chúng tôi đưa chúng vào mẫu, mà không thực sự sử dụng chúng.

Và thay thế là một lookahead tiêu cực mà không bao giờ đạt đến điểm mà sự biểu hiện được định nghĩa:

/ 
(?! 
    (?!)  # fail - this makes the surrounding lookahead pass unconditionally 
    # the engine never gets here; now we can write down our definitions 
    (?<my>\d\d) 
) 
(?&my)-(?&my)-(?&my) 
/x 

Tuy nhiên, bạn chỉ thực sự cần mẫu này, nếu bạn không có điều kiện, nhưng làm có tên là mô hình tái sử dụng (và tôi không nghĩ rằng một hương vị như thế này tồn tại). Các biến thể khác có lợi thế, việc sử dụng các DEFINE làm cho nó rõ ràng những gì nhóm là cho, trong khi cách tiếp cận lookahead là một chút obfuscated.

Vì vậy, trở lại ví dụ ban đầu của bạn:

/ 
# Definitions 
(?(DEFINE) 
    (?<BEGIN>[$][(]) 
) 
# And now your pattern 
    (?: (?&BEGIN) ([^)]+) \)) # the field access specifier 
| 
    (
    (?: # any one single character before the '$(' 
     \n | . (?= (?&BEGIN)) 
    ) 
    | 
    (?: # any one single character, except the one before the '$(' 
     \n | . (?! (?&BEGIN)) 
    )* 
) 
/x 

Có hai hãy cẩn thận chính đối với phương pháp này:

  1. tài liệu tham khảo đệ quy là atomic. Đó là, một khi tài liệu tham khảo đã kết hợp một cái gì đó nó sẽ không bao giờ được quay trở lại. Đối với một số trường hợp nhất định, điều này có nghĩa là bạn phải khéo léo trong việc tạo biểu thức của mình, do đó, trận đấu đầu tiên sẽ luôn là thứ bạn muốn.
  2. Bạn không thể sử dụng chụp bên trong các mẫu được xác định. Nếu bạn sử dụng một cái gì đó như (?<myPattern>a(b)c) và tái sử dụng nó, b sẽ không bao giờ bị bắt - khi sử dụng lại mẫu, tất cả các nhóm đều không được chụp.

Lợi thế quan trọng nhất so với bất kỳ loại nội suy hoặc nối nào là bạn không bao giờ có thể tạo ra các mẫu không hợp lệ với điều này và bạn cũng không thể làm rối nhóm chụp của mình.

+1

+1 cho câu trả lời rất toàn diện. Tôi ước tôi có thể +10 –

+2

Câu trả lời này đã được thêm vào [Câu hỏi thường gặp về biểu hiện thường xuyên tràn ngăn xếp] (http://stackoverflow.com/a/22944075/2736496), trong "Kiểm soát động từ và đệ quy". – aliteralmind

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