2009-07-20 8 views
29

Nếu bạn muốn kiểm tra xem một cái gì đó phù hợp với một regex, nếu như vậy, in nhóm đầu tiên, bạn làm ..Thay thế cho `match = re.match(); nếu kết hợp: ... `thành ngữ?

import re 
match = re.match("(\d+)g", "123g") 
if match is not None: 
    print match.group(1) 

này là hoàn toàn pedantic, nhưng match biến trung gian là một chút khó chịu ..

Ngôn ngữ như Perl thực hiện điều này bằng cách tạo mới $1 .. $9 biến cho các nhóm phù hợp, giống như ..

if($blah ~= /(\d+)g/){ 
    print $1 
} 

Từ this reddit comment,

with re_context.match('^blah', s) as match: 
    if match: 
     ... 
    else: 
     ... 

đồi khế, đồi tôi nghĩ là một ý tưởng thú vị, vì vậy tôi đã viết một thực hiện đơn giản của nó:

#!/usr/bin/env python2.6 
import re 

class SRE_Match_Wrapper: 
    def __init__(self, match): 
     self.match = match 

    def __exit__(self, type, value, tb): 
     pass 

    def __enter__(self): 
     return self.match 

    def __getattr__(self, name): 
     if name == "__exit__": 
      return self.__exit__ 
     elif name == "__enter__": 
      return self.__name__ 
     else: 
      return getattr(self.match, name) 

def rematch(pattern, inp): 
    matcher = re.compile(pattern) 
    x = SRE_Match_Wrapper(matcher.match(inp)) 
    return x 
    return match 

if __name__ == '__main__': 
    # Example: 
    with rematch("(\d+)g", "123g") as m: 
     if m: 
      print(m.group(1)) 

    with rematch("(\d+)g", "123") as m: 
     if m: 
      print(m.group(1)) 

(Chức năng này có thể lý thuyết được vá vào đối tượng _sre.SRE_Match)

Nó sẽ tốt đẹp nếu bạn có thể bỏ qua việc thực hiện khối mã của câu lệnh with, nếu không có kết quả phù hợp, điều này sẽ đơn giản hóa việc này thành ..

with rematch("(\d+)g", "123") as m: 
    print(m.group(1)) # only executed if the match occurred 

.. nhưng điều này dường như không thể dựa trên những gì tôi có thể suy ra từ PEP 343

Bất kỳ ý tưởng nào? Như tôi đã nói, đây thực sự là sự phiền toái nhỏ nhặt, gần như đến mức chơi gôn mã ..

+1

"gần như đến thời điểm chơi mã" không đồng ý. Đây hoàn toàn là chơi gôn. Không thể thấy những gì "gây phiền nhiễu" về việc thiết lập một biến đại diện cho thay đổi trạng thái. –

+0

Tôi đã nói rằng tôi đã bị lừa đảo .. – dbr

+2

Thảo luận tương tự đã có trên SO: http://stackoverflow.com/questions/447086/pythons-re-module-saving-state –

Trả lời

12

Tôi không nghĩ nó tầm thường. Tôi không muốn phải rắc một điều kiện dư thừa xung quanh mã của mình nếu tôi thường viết mã như vậy.

này là hơi kỳ quặc, nhưng bạn có thể làm điều này với một iterator:

import re 

def rematch(pattern, inp): 
    matcher = re.compile(pattern) 
    matches = matcher.match(inp) 
    if matches: 
     yield matches 

if __name__ == '__main__': 
    for m in rematch("(\d+)g", "123g"): 
     print(m.group(1)) 

Điều kỳ lạ là nó sử dụng một iterator kiếm cái gì đó không được lặp lại - đó là gần gũi hơn với một điều kiện, và ngay từ cái nhìn đầu tiên nó có thể trông giống như nó sẽ mang lại nhiều kết quả cho mỗi trận đấu.

Có vẻ lạ khi người quản lý ngữ cảnh không thể khiến chức năng được quản lý của nó bị bỏ qua hoàn toàn; trong khi đó không phải là một trong những trường hợp sử dụng "với", nó có vẻ giống như một phần mở rộng tự nhiên.

+0

Vâng, nó sẽ hoạt động nếu mã '__enter__' được thực hiện bên trong phần' try' cho phép kiểm soát mã '__exit__' đối với các ngoại lệ (vì mã' __enter__' sau đó có thể ném một loại ngoại lệ đặc biệt bị nuốt bởi câu lệnh 'with', có hiệu quả ngăn chặn bất kỳ mã nào bên trong nó thực hiện.) Ngay bây giờ, tôi không thấy một cách để vượt qua điều đó. – Blixt

+3

Sẽ tốt hơn nếu Python cho phép gán trong các biểu thức, như C: "nếu x = y():", "nếu không (x = y():"; nó sẽ xử lý đơn giản. –

+0

+1: là giải pháp khả thi duy nhất để tiết kiệm một dòng mã.Trong khi sử dụng một máy phát điện không phải là rất trực quan, nó thực hiện công việc và cả hai phạm vi và thread-an toàn. – Blixt

0

Tôi không nghĩ rằng việc sử dụng with là giải pháp trong trường hợp này. Bạn sẽ phải tăng một ngoại lệ trong phần BLOCK (được chỉ định bởi người dùng) và có phương thức __exit__ trả về True để "nuốt" ngoại lệ. Vì vậy, nó sẽ không bao giờ nhìn tốt.

Tôi khuyên bạn nên thực hiện cú pháp tương tự như cú pháp Perl. Làm của riêng mở rộng re mô-đun của bạn (Tôi sẽ gọi nó rex) và có nó thiết lập các biến trong mô-đun không gian tên của nó:

if rex.match('(\d+)g', '123g'): 
    print rex._1 

Như bạn có thể thấy trong các ý kiến ​​dưới đây, phương pháp này không phải là scope- cũng không thread-safe . Bạn sẽ chỉ sử dụng điều này nếu bạn hoàn toàn chắc chắn rằng ứng dụng của bạn sẽ không trở thành đa luồng trong tương lai và bất kỳ chức năng nào được gọi từ phạm vi mà bạn đang sử dụng này sẽ cũng sử dụng cùng một phương pháp.

+1

Hãy cẩn thận về điều này. Nó sẽ phá vỡ nếu mã bên trong tái sử dụng có điều kiện đối tượng "rex", do đó, để an toàn trong trường hợp đó, bạn cần phải tạo một bản sao khi bạn sử dụng nó - và đó là chủ yếu là hoàn tác lợi ích. Nó cũng không an toàn trừ khi bạn nhảy một số vòng TLS; match() trên một đối tượng lại (nên) hoàn toàn an toàn. Tôi nghĩ rằng những vấn đề này vượt xa những lợi ích của việc làm theo cách này - đó là rất nhiều để phải ghi nhớ để lưu một dòng mã. –

+0

và địa phương chủ đề để xử lý đa luồng? –

+0

Đối với một số trường hợp, tôi tin rằng lợi ích lớn hơn các vấn đề. Đối với các chương trình đơn luồng đơn giản, tôi tin rằng cách tiếp cận này là OK. Tuy nhiên "match = re.match (...); nếu match: ..." là python thành ngữ. Tôi sẽ tiếp tục làm theo cách đó. Vẫn +1 cho câu trả lời của @ Blixt, cho một câu trả lời thanh lịch, giống như câu hỏi ban đầu. – codeape

0

Nếu bạn đang làm rất nhiều những ở một nơi, đây là một câu trả lời khác:

import re 
class Matcher(object): 
    def __init__(self): 
     self.matches = None 
    def set(self, matches): 
     self.matches = matches 
    def __getattr__(self, name): 
     return getattr(self.matches, name) 

class re2(object): 
    def __init__(self, expr): 
     self.re = re.compile(expr) 

    def match(self, matcher, s): 
     matches = self.re.match(s) 
     matcher.set(matches) 
     return matches 

pattern = re2("(\d+)g") 
m = Matcher() 
if pattern.match(m, "123g"): 
    print(m.group(1)) 
if not pattern.match(m, "x123g"): 
    print "no match" 

Bạn có thể biên dịch regex một lần với cùng một chủ đề an toàn như re, tạo ra một đối tượng Matcher tái sử dụng duy nhất cho toàn bộ chức năng, và sau đó bạn có thể sử dụng nó rất ngắn gọn. Điều này cũng có lợi ích mà bạn có thể đảo ngược nó theo cách hiển nhiên - để làm điều đó với một trình lặp, bạn cần phải vượt qua một lá cờ để yêu cầu nó đảo ngược kết quả của nó.

Sẽ không có nhiều trợ giúp nếu bạn chỉ thực hiện một kết hợp duy nhất cho mỗi hàm; bạn không muốn giữ các đối tượng Matcher trong một bối cảnh rộng hơn thế; nó sẽ gây ra các vấn đề tương tự như giải pháp của Blixt.

0

Đây không phải là thực sự khá ưa nhìn, nhưng bạn có thể lợi nhuận từ các getattr(object, name[, default]) chức năng tích hợp trong việc sử dụng nó như thế này:

>>> getattr(re.match("(\d+)g", "123g"), 'group', lambda n:'')(1) 
'123' 
>>> getattr(re.match("(\d+)g", "X23g"), 'group', lambda n:'')(1) 
'' 

Để bắt chước các nếu nhóm phù hợp với in dòng chảy, bạn có thể (ab) sử dụng câu lệnh for theo cách này:

>>> for group in filter(None, [getattr(re.match("(\d+)g", "123g"), 'group', None)]): 
     print(group(1)) 
123 
>>> for group in filter(None, [getattr(re.match("(\d+)g", "X23g"), 'group', None)]): 
     print(group(1)) 
>>> 

Tất nhiên bạn có thể định nghĩa một hàm nhỏ để làm công việc bẩn:

>>> matchgroup = lambda p,s: filter(None, [getattr(re.match(p, s), 'group', None)]) 
>>> for group in matchgroup("(\d+)g", "123g"): 
     print(group(1)) 
123 
>>> for group in matchgroup("(\d+)g", "X23g"): 
     print(group(1)) 
>>> 
0

Không phải là giải pháp hoàn hảo, nhưng không cho phép bạn chuỗi một vài lựa chọn phù hợp cho cùng str:

class MatchWrapper(object): 
    def __init__(self): 
    self._matcher = None 

    def wrap(self, matcher): 
    self._matcher = matcher 

    def __getattr__(self, attr): 
    return getattr(self._matcher, attr) 

def match(pattern, s, matcher): 
    m = re.match(pattern, s) 
    if m: 
    matcher.wrap(m) 
    return True 
    else: 
    return False 

matcher = MatchWrapper() 
s = "123g"; 
if _match("(\d+)g", line, matcher): 
    print matcher.group(1) 
elif _match("(\w+)g", line, matcher): 
    print matcher.group(1) 
else: 
    print "no match" 
4

Một cú pháp đẹp sẽ là một cái gì đó như thế này:

header = re.compile('(.*?) = (.*?)$') 
footer = re.compile('(.*?): (.*?)$') 

if header.match(line) as m: 
    key, value = m.group(1,2) 
elif footer.match(line) as m 
    key, value = m.group(1,2) 
else: 
    key, value = None, None 
1

tôi có một cách để làm điều này, dựa trên Glen Maynard của giải pháp:

for match in [m for m in [re.match(pattern,key)] if m]: 
    print "It matched: %s" % match 

Tương tự như giải pháp của Glen, itte này tỷ lệ là 0 (nếu không có trận đấu) hoặc 1 (nếu trận đấu) lần.

Không cần phụ, nhưng ít gọn gàng hơn.

0

Đây là giải pháp của tôi:

import re 

s = 'hello world' 

match = [] 
if match.append(re.match('w\w+', s)) or any(match): 
    print('W:', match.pop().group(0)) 
elif match.append(re.match('h\w+', s)) or any(match): 
    print('H:', match.pop().group(0)) 
else: 
    print('No match found') 

Bạn có thể sử dụng như nhiều elif khoản khi cần thiết.

Thậm chí tốt hơn:

import re 

s = 'hello world' 

if vars().update(match=re.match('w\w+', s)) or match: 
    print('W:', match.group(0)) 
elif vars().update(match=re.match('h\w+', s)) or match: 
    print('H:', match.group(0)) 
else: 
    print('No match found') 

Cả thêmcập nhật trở Không. Vì vậy, bạn phải thực sự kiểm tra kết quả biểu đạt của mình bằng cách sử dụng phần hoặc trong mọi trường hợp.

Thật không may, điều này chỉ hoạt động miễn là mã nằm ở cấp cao nhất, tức là không có trong hàm.

0

Đây là những gì tôi làm:

def re_match_cond (match_ref, regex, text): 
    match = regex.match (text) 
    del match_ref[:] 
    match_ref.append (match) 
    return match 

if __name__ == '__main__': 
    match_ref = [] 
    if re_match_cond (match_ref, regex_1, text): 
     match = match_ref[0] 
     ### ... 
    elif re_match_cond (match_ref, regex_2, text): 
     match = match_ref[0] 
     ### ... 
    elif re_match_cond (match_ref, regex_3, text): 
     match = match_ref[0] 
     ### ... 
    else: 
     ### no match 
     ### ... 

Đó là, tôi vượt qua một danh sách để các chức năng để thi đua pass-by-reference.