รูปแบบการแยก Python ตรงกัน


129

Python 2.7.1 ฉันพยายามใช้นิพจน์ทั่วไปของ python เพื่อแยกคำที่อยู่ภายในรูปแบบ

ฉันมีสตริงที่มีลักษณะเช่นนี้

someline abc
someother line
name my_user_name is valid
some more lines

ฉันต้องการแยกคำว่า "my_user_name" ฉันทำสิ่งที่ชอบ

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>

ฉันจะแตก my_user_name ได้อย่างไร

คำตอบ:


159

คุณต้องจับภาพจาก regex สำหรับรูปแบบหากพบดึงสตริงใช้search group(index)สมมติว่ามีการตรวจสอบที่ถูกต้อง:

>>> 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
แน่ใจหรือว่าไม่ใช่group(0)สำหรับนัดแรก?
sharshofski

33
สาย แต่ทั้งใช่และไม่ใช่ group(0)ส่งคืนข้อความที่ตรงกันไม่ใช่กลุ่มแรกที่บันทึก ความคิดเห็นของรหัสถูกต้องในขณะที่คุณดูเหมือนจะสับสนกลุ่มการจับภาพและการจับคู่ group(1)ส่งคืนกลุ่มการจับภาพแรก
andrewgu

1
ฉันได้รับNameError: name '_' is not defined
Ian G

_ = p.search(s)บรรทัดที่สองของคุณผมคิดว่าควรอ่าน ฉันเห็นมันกล่าวถึงการตั้งค่าผลลัพธ์เป็น_แต่รหัสไม่ได้สะท้อนถึงสิ่งนั้น ฉันเปลี่ยนเป็น_ = p.search(s)สำหรับบรรทัดที่สองและได้ผล
Ian G

2
@IanG ฉันขอโทษฉันจะอัปเดตคำตอบของฉัน BTW ด้วย python REPL มาตรฐานผลลัพธ์สุดท้ายจะถูกเก็บไว้ในตัวแปรพิเศษที่เรียกว่า_. ไม่สามารถใช้ได้นอกที่อื่น
UltraInstinct

57

คุณสามารถใช้กลุ่มที่ตรงกัน:

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

เช่น

>>> 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']

ที่นี่ฉันใช้re.findallแทนที่จะre.searchได้รับอินสแตนซ์ทั้งหมดของmy_user_nameไฟล์. เมื่อใช้re.searchคุณจะต้องได้รับข้อมูลจากกลุ่มในวัตถุที่ตรงกัน:

>>> 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'

ดังที่ได้กล่าวไว้ในความคิดเห็นคุณอาจต้องการทำให้ regex ของคุณไม่โลภ:

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

เพื่อเลือกเฉพาะสิ่งที่อยู่ระหว่าง'name 'และถัดไป' is valid'(แทนที่จะปล่อยให้ regex ของคุณเลือกคนอื่น ๆ' is valid'ในกลุ่มของคุณ


2
เป็นไปได้ว่าจำเป็นต้องมีการจับคู่แบบไม่โลภ ... (เว้นแต่ชื่อผู้ใช้สามารถเป็นได้หลายคำ ... )
Jon Clements

@JonClements - คุณหมายถึง(.*?)? ใช่เป็นไปได้แม้ว่าจะไม่จำเป็นเว้นแต่ OP ที่เราใช้re.DOTALL
mgilson

ใช่ - re.findall('name (.*) is valid', 'name jon clements is valid is valid is valid')อาจไม่ได้ผลลัพธ์ที่ต้องการ ...
Jon Clements

สิ่งนี้ใช้ไม่ได้กับ Python 2.7.1? มันพิมพ์วัตถุรูปแบบ?
กรรณเอกนาถ

@CalmStorm - ส่วนไหนใช้ไม่ได้ (ฉันทดสอบบน python2.7.3) ส่วนที่ฉันใช้.groupนั้นเหมือนกับคำตอบที่คุณยอมรับทุก
ประการ

16

คุณสามารถใช้สิ่งนี้:

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

อาจจะสั้นกว่าและเข้าใจง่ายกว่าเล็กน้อย:

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

คุณสามารถใช้กลุ่ม (ระบุด้วย'('และ')') เพื่อจับภาพส่วนต่างๆของสตริง วิธีการจับคู่วัตถุgroup()จะให้เนื้อหาของกลุ่ม:

>>> 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'

ใน Python 3.6+ คุณสามารถสร้างดัชนีลงในวัตถุที่ตรงกันแทนการใช้group():

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

6

นี่คือวิธีที่ทำได้โดยไม่ต้องใช้กลุ่ม (Python 3.6 ขึ้นไป):

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

1
สิ่งนี้กล่าวถึง Python Regex แต่ไม่ได้ตอบคำถามเฉพาะของ OP
Aleister Tanek Javas Mraz

นอกจากนี้โดยพื้นฐานแล้วสิ่งนี้ไม่ได้เพิ่มอะไรใหม่ให้กับคำตอบที่มีอยู่ซึ่งกล่าวถึงไวยากรณ์การจัดทำดัชนี 3.6+
Eugene Yarmash

3

นอกจากนี้คุณยังสามารถใช้กลุ่มการจับภาพและการเข้าถึงกลุ่มเช่นพจนานุกรม(?P<user>pattern)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

ดูเหมือนว่าคุณกำลังพยายามดึงชื่อรองเพียงแค่หาคู่ re.finditerหากเป็นกรณีนี้มีการจัดทำดัชนีช่วงสำหรับการแข่งขันของคุณจะเป็นประโยชน์และผมขอแนะนำให้ใช้ ทางลัดคุณรู้ว่าnameส่วนของ regex คือความยาว 5 และis validความยาว 9 ดังนั้นคุณสามารถแบ่งข้อความที่ตรงกันเพื่อแยกชื่อ

หมายเหตุ - ในตัวอย่างของคุณดูเหมือนว่าsเป็นสตริงที่มีตัวแบ่งบรรทัดนั่นคือสิ่งที่สันนิษฐานด้านล่าง

## 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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.