Mô hình trích xuất Python phù hợp


129

Python 2.7.1 Tôi đang cố gắng sử dụng biểu thức chính quy python để trích xuất các từ bên trong một mẫu

Tôi có một số chuỗi trông như thế này

someline abc
someother line
name my_user_name is valid
some more lines

Tôi muốn trích xuất từ ​​"my_user_name". Tôi làm một cái gì đó như

import re
s = #that big string
p = re.compile("name .* is valid", re.flags)
p.match(s) #this gives me <_sre.SRE_Match object at 0x026B6838>

Làm cách nào để trích xuất my_user_name bây giờ?

Câu trả lời:


159

Bạn cần phải chụp từ regex. searchđối với mẫu, nếu tìm thấy, lấy chuỗi bằng cách sử dụng group(index). Giả sử kiểm tra hợp lệ được thực hiện:

>>> p = re.compile("name (.*) is valid")
>>> result = p.search(s)
>>> result
<_sre.SRE_Match object at 0x10555e738>
>>> result.group(1)     # group(1) will return the 1st capture.
                        # group(0) will returned the entire matched text.
'my_user_name'

26
Bạn có chắc chắn rằng đó không phải group(0)là trận đấu đầu tiên?
sharshofski

33
Loại muộn, nhưng cả có và không. group(0)trả về văn bản phù hợp, không phải nhóm chụp đầu tiên. Nhận xét mã là chính xác, trong khi bạn có vẻ khó hiểu các nhóm chụp và khớp. group(1)trả về nhóm chụp đầu tiên.
andrewgu

1
Tôi nhận đượcNameError: name '_' is not defined
Ian G

Dòng thứ hai của bạn tôi nghĩ nên đọc _ = p.search(s). Tôi thấy nó đề cập đến việc thiết lập kết quả _nhưng mã không phản ánh điều đó. Tôi đã thay đổi _ = p.search(s)cho dòng thứ hai và nó hoạt động.
Ian G

2
@IanG Tôi xin lỗi, tôi sẽ cập nhật câu trả lời của tôi. BTW, với REPL python tiêu chuẩn, kết quả cuối cùng được lưu trữ trong một biến đặc biệt gọi là _. Nó không hợp lệ bên ngoài bất cứ nơi nào khác.
UltraInstotype

57

Bạn có thể sử dụng các nhóm phù hợp:

p = re.compile('name (.*) is valid')

ví dụ

>>> import re
>>> p = re.compile('name (.*) is valid')
>>> s = """
... someline abc
... someother line
... name my_user_name is valid
... some more lines"""
>>> p.findall(s)
['my_user_name']

Ở đây tôi sử dụng re.findallchứ không phải re.searchđể có được tất cả các trường hợp my_user_name. Sử dụng re.search, bạn cần lấy dữ liệu từ nhóm trên đối tượng khớp:

>>> p.search(s)   #gives a match object or None if no match is found
<_sre.SRE_Match object at 0xf5c60>
>>> p.search(s).group() #entire string that matched
'name my_user_name is valid'
>>> p.search(s).group(1) #first group that match in the string that matched
'my_user_name'

Như đã đề cập trong các bình luận, bạn có thể muốn làm cho regex của mình không tham lam:

p = re.compile('name (.*?) is valid')

chỉ chọn những thứ ở giữa 'name 'và tiếp theo ' is valid'(thay vì cho phép regex của bạn lấy thứ khác ' is valid'trong nhóm của bạn.


2
Có thể cần phải có một trận đấu không tham lam ... (trừ khi tên người dùng có thể có nhiều từ ...)
Jon Clements

@JonClements - Ý bạn là (.*?)gì? Vâng, điều đó là có thể, mặc dù không cần thiết trừ khi OP chúng tôi sử dụngre.DOTALL
mgilson

yeah - re.findall('name (.*) is valid', 'name jon clements is valid is valid is valid')có lẽ sẽ không mang lại kết quả mong muốn ...
Jon Clements

Điều này không hoạt động cho Python 2.7.1? Nó chỉ in một đối tượng mô hình?
Kannan Ekanath

@CalmStorm - Phần nào không hoạt động (Tôi đã thử nghiệm trên python2.7.3)? Phần tôi sử dụng .grouphoàn toàn giống với câu trả lời bạn đã chấp nhận ...
mgilson

16

Bạn có thể sử dụng một cái gì đó như thế này:

import re
s = #that big string
# the parenthesis create a group with what was matched
# and '\w' matches only alphanumeric charactes
p = re.compile("name +(\w+) +is valid", re.flags)
# use search(), so the match doesn't have to happen 
# at the beginning of "big string"
m = p.search(s)
# search() returns a Match object with information about what was matched
if m:
    name = m.group(1)
else:
    raise Exception('name not found')

10

Có lẽ đó là một chút ngắn hơn và dễ hiểu hơn:

import re
text = '... someline abc... someother line... name my_user_name is valid.. some more lines'
>>> re.search('name (.*) is valid', text).group(1)
'my_user_name'

9

Bạn muốn một nhóm chụp .

p = re.compile("name (.*) is valid", re.flags) # parentheses for capture groups
print p.match(s).groups() # This gives you a tuple of your matches.

9

Bạn có thể sử dụng các nhóm (được chỉ định bằng '('')') để chụp các phần của chuỗi. group()Phương thức của đối tượng khớp sau đó cung cấp cho bạn nội dung của nhóm:

>>> import re
>>> s = 'name my_user_name is valid'
>>> match = re.search('name (.*) is valid', s)
>>> match.group(0)  # the entire match
'name my_user_name is valid'
>>> match.group(1)  # the first parenthesized subgroup
'my_user_name'

Trong Python 3.6+, bạn cũng có thể lập chỉ mục vào một đối tượng khớp thay vì sử dụng group():

>>> match[0]  # the entire match 
'name my_user_name is valid'
>>> match[1]  # the first parenthesized subgroup
'my_user_name'

6

Đây là một cách để làm điều đó mà không cần sử dụng các nhóm (Python 3.6 trở lên):

>>> re.search('2\d\d\d[01]\d[0-3]\d', 'report_20191207.xml')[0]
'20191207'

1
Điều này giải quyết Python Regex, nhưng không giải quyết câu hỏi cụ thể của OP.
Aleister Tanek Javas Mraz

Ngoài ra, điều này về cơ bản không có gì mới đối với (các) câu trả lời hiện có đề cập đến cú pháp lập chỉ mục 3.6+.
Eugene Yarmash

3

Bạn cũng có thể sử dụng một nhóm chụp (?P<user>pattern)và truy cập nhóm như một từ điển match['user'].

string = '''someline abc\n
            someother line\n
            name my_user_name is valid\n
            some more lines\n'''

pattern = r'name (?P<user>.*) is valid'
matches = re.search(pattern, str(string), re.DOTALL)
print(matches['user'])

# my_user_name

1

Có vẻ như bạn đang thực sự cố gắng trích xuất một tên phó chỉ đơn giản là tìm một kết quả khớp. Nếu đây là trường hợp, có chỉ số nhịp cho trận đấu của bạn là hữu ích và tôi khuyên bạn nên sử dụng re.finditer. Là một phím tắt, bạn biết namephần regex của bạn có độ dài 5 và is validchiều dài là 9, vì vậy bạn có thể cắt văn bản phù hợp để trích xuất tên.

Lưu ý - Trong ví dụ của bạn, có vẻ như slà chuỗi có ngắt dòng, vì vậy đó là những gì được giả định dưới đây.

## covert s to list of strings separated by line:
s2 = s.splitlines()

## find matches by line: 
for i, j in enumerate(s2):
    matches = re.finditer("name (.*) is valid", j)
    ## ignore lines without a match
    if matches:
        ## loop through match group elements
        for k in matches:
            ## get text
            match_txt = k.group(0)
            ## get line span
            match_span = k.span(0)
            ## extract username
            my_user_name = match_txt[5:-9]
            ## compare with original text
            print(f'Extracted Username: {my_user_name} - found on line {i}')
            print('Match Text:', match_txt)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.