2013-01-06 35 views
7

Tôi đã gặp sự cố nhỏ khi sử dụng Python Regex.Kết hợp nhiều mẫu regex với toán tử luân phiên?

Giả sử đây là đầu vào:

(zyx)bc 

Những gì tôi đang cố gắng để đạt được bất cứ điều gì có được là giữa dấu ngoặc như một trận đấu duy nhất, và bất kỳ char bên ngoài như một trận đấu cá nhân. Kết quả mong muốn sẽ nằm dọc theo các dòng:

['zyx','b','c'] 

Thứ tự khớp cần được giữ lại.

Tôi đã thử có được điều này với Python 3.3, nhưng dường như không thể tìm ra Regex chính xác. Cho đến nay tôi có:

matches = findall(r'\((.*?)\)|\w', '(zyx)bc') 

print(matches) mang lại như sau:

['zyx','',''] 

Bất kỳ ý tưởng những gì tôi đang làm sai?

+0

Tại sao không chỉ 'xyz | a | b'? – fge

+0

Nó chỉ là một đầu vào mẫu. Regex có thể phân biệt giữa các trường hợp khác nhau, ví dụ như (ab) (bc) (ca), abc, (abc) (abc) (abc), hoặc (zyx) bc, vv trong khi nhận ra ký tự nào nằm trong dấu ngoặc đơn và không. –

Trả lời

11

Từ các tài liệu của re.findall:

Nếu một hoặc nhiều nhóm có mặt trong mô hình, trả về một danh sách của các nhóm; đây sẽ là danh sách các bộ dữ liệu nếu mẫu có nhiều nhóm.

Khi regexp của bạn khớp với chuỗi ba lần, nhóm (.*?) trống cho hai kết quả khớp thứ hai. Nếu bạn muốn đầu ra của nửa kia của regexp, bạn có thể thêm một nhóm thứ hai:

>>> re.findall(r'\((.*?)\)|(\w)', '(zyx)bc') 
[('zyx', ''), ('', 'b'), ('', 'c')] 

Ngoài ra, bạn có thể loại bỏ tất cả các nhóm để có được một danh sách đơn giản của chuỗi lại:

>>> re.findall(r'\(.*?\)|\w', '(zyx)bc') 
['(zyx)', 'b', 'c'] 

Mặc dù vậy, bạn sẽ cần xóa dấu ngoặc đơn theo cách thủ công.

+0

FYI: Cảm ơn bạn đã trả lời. Để loại bỏ các dấu ngoặc đơn: 'matches = [match.strip ('() ') cho phù hợp trong findall (r' \ (. *? \) | \ W ', case)]' –

1

Các tài liệu đề cập đến điều trị nhóm đặc biệt, vì vậy đừng đặt một nhóm xung quanh mô hình trong ngoặc đơn, và bạn sẽ nhận được tất cả mọi thứ, nhưng bạn sẽ cần phải loại bỏ các dấu ngoặc từ các dữ liệu phù hợp cho mình:

>>> re.findall(r'\(.+?\)|\w', '(zyx)bc') 
['(zyx)', 'b', 'c'] 

hoặc sử dụng các nhóm hơn, sau đó xử lý các bộ kết quả để có được những chuỗi mà bạn tìm kiếm:

>>> [''.join(t) for t in re.findall(r'\((.+?)\)|(\w)', '(zyx)bc')] 
>>> ['zyx', 'b', 'c'] 
1
In [108]: strs="(zyx)bc" 

In [109]: re.findall(r"\(\w+\)|\w",strs) 
Out[109]: ['(zyx)', 'b', 'c'] 

In [110]: [x.strip("()") for x in re.findall(r"\(\w+\)|\w",strs)] 
Out[110]: ['zyx', 'b', 'c'] 
2

chúng ta hãy nhìn vào sản lượng của chúng tôi sử dụng re.DEBUG.

branch 
    literal 40 
    subpattern 1 
    min_repeat 0 65535 
     any None 
    literal 41 
or 
    in 
    category category_word 

Ouch, chỉ có một subpattern ở trong đó nhưng re.findall chỉ kéo ra subpattern s nếu có!

a = re.findall(r'\((.*?)\)|(.)', '(zyx)bc',re.DEBUG); a 
[('zyx', ''), ('', 'b'), ('', 'c')] 
branch 
    literal 40 
    subpattern 1 
    min_repeat 0 65535 
     any None 
    literal 41 
or 
    subpattern 2 
    any None 

Tốt hơn. :)

Bây giờ, chúng tôi chỉ phải thực hiện điều này theo định dạng bạn muốn.

[i[0] if i[0] != '' else i[1] for i in a] 
['zyx', 'b', 'c'] 
1

Các câu trả lời khác cho bạn biết cách nhận kết quả bạn cần, nhưng với bước bổ sung là xóa dấu ngoặc đơn theo cách thủ công. Nếu bạn sử dụng lookarounds trong regex của bạn, bạn sẽ không cần phải dải ngoặc tay:

>>> import re 
>>> s = '(zyx)bc' 
>>> print (re.findall(r'(?<=\()\w+(?=\))|\w', s)) 
['zyx', 'b', 'c'] 

Giải thích:

(?<=\() // lookbehind for left parenthesis 
\w+  // all characters until: 
(?=\)) // lookahead for right parenthesis 
|  // OR 
\w  // any character 
Các vấn đề liên quan