ส่งคืนสตริงด้วยการจับคู่ครั้งแรก Regex


91

ฉันต้องการรับการแข่งขันครั้งแรกของ regex

ในกรณีนี้ฉันมีรายชื่อ:

text = 'aa33bbb44'
re.findall('\d+',text)

['33', '44']

ฉันสามารถแยกองค์ประกอบแรกของรายการ:

text = 'aa33bbb44'
re.findall('\d+',text)[0]

'33'

แต่จะใช้ได้เฉพาะเมื่อมีการแข่งขันอย่างน้อยหนึ่งรายการมิฉะนั้นฉันจะได้รับข้อผิดพลาด:

text = 'aazzzbbb'
re.findall('\d+',text)[0]

IndexError: ดัชนีรายการอยู่นอกช่วง

ในกรณีนี้ฉันสามารถกำหนดฟังก์ชัน:

def return_first_match(text):
    try:
        result = re.findall('\d+',text)[0]
    except Exception, IndexError:
        result = ''
    return result

มีวิธีการรับผลลัพธ์โดยไม่กำหนดฟังก์ชันใหม่หรือไม่?


สำหรับฉันคำตอบที่ยอมรับไม่ได้ผล ฉันต้องลบการเข้าถึงดัชนีอาร์เรย์และใช้การlen(re.findAll)==0ตรวจสอบแทน
Vishal

คำตอบ:


109

คุณสามารถฝัง''ค่าเริ่มต้นใน regex ของคุณได้โดยเพิ่ม|$:

>>> re.findall('\d+|$', 'aa33bbb44')[0]
'33'
>>> re.findall('\d+|$', 'aazzzbbb')[0]
''
>>> re.findall('\d+|$', '')[0]
''

ยังใช้งานได้กับre.searchผู้อื่น:

>>> re.search('\d+|$', 'aa33bbb44').group()
'33'
>>> re.search('\d+|$', 'aazzzbbb').group()
''
>>> re.search('\d+|$', '').group()
''

เยี่ยมมาก search / .group มีข้อได้เปรียบเหนือ findall / [0] หรือไม่
Luis Ramon Ramirez Rodriguez

6
@LuisRamonRamirezRodriguez มันสามารถหยุดได้ทันทีที่พบการแข่งขันไม่ต้องประมวลผลข้อความที่เหลือและไม่จำเป็นต้องจัดเก็บการแข่งขันทั้งหมด ดังนั้นจึงมีประสิทธิภาพมากขึ้น นอกจากนี้"คือสิ่งที่คุณต้องการ"ตามที่ @TimPeters กล่าว นั่นอาจเป็นข้อดีเมื่อคุณหรือคนอื่นอ่านแล้วสงสัยว่า"ทำไมถึงfindallใช้" .
Stefan Pochmann

43

หากคุณต้องการเพียงคู่แรกให้ใช้re.searchแทนre.findall:

>>> m = re.search('\d+', 'aa33bbb44')
>>> m.group()
'33'
>>> m = re.search('\d+', 'aazzzbbb')
>>> m.group()
Traceback (most recent call last):
  File "<pyshell#281>", line 1, in <module>
    m.group()
AttributeError: 'NoneType' object has no attribute 'group'

จากนั้นคุณสามารถใช้mเป็นเงื่อนไขการตรวจสอบได้ดังนี้:

>>> m = re.search('\d+', 'aa33bbb44')
>>> if m:
        print('First number found = {}'.format(m.group()))
    else:
        print('Not Found')


First number found = 33

13

ฉันจะไปกับ:

r = re.search("\d+", ch)
result = return r.group(0) if r else ""

re.searchเพียง แต่ดูสำหรับครั้งแรกของfindallการแข่งขันในสายอยู่แล้วดังนั้นผมคิดว่ามันทำให้ความตั้งใจของคุณเล็กน้อยชัดเจนมากขึ้นกว่าการใช้


9

คุณไม่ควรใช้.findall()เลย - .search()คือสิ่งที่คุณต้องการ พบการจับคู่ซ้ายสุดซึ่งเป็นสิ่งที่คุณต้องการ (หรือส่งคืนNoneหากไม่มีการจับคู่

m = re.search(pattern, text)
result = m.group(0) if m else ""

ไม่ว่าคุณจะใส่ในฟังก์ชันนั้นขึ้นอยู่กับคุณหรือไม่ เป็นเรื่องผิดปกติที่จะต้องการส่งคืนสตริงว่างหากไม่พบการจับคู่ซึ่งเป็นเหตุผลว่าทำไมจึงไม่มีอะไรเหมือนในตัวมันเป็นไปไม่ได้ที่จะสับสนว่า.search()ตัวมันเองจะพบการจับคู่หรือไม่ (จะส่งคืนNoneหากไม่พบหรือSRE_Matchวัตถุ ถ้าเป็นเช่นนั้น)


3

คุณทำได้:

x = re.findall('\d+', text)
result = x[0] if len(x) > 0 else ''

โปรดทราบว่าคำถามของคุณไม่เกี่ยวข้องกับนิพจน์ทั่วไป แต่คุณจะค้นหาองค์ประกอบจากอาร์เรย์อย่างปลอดภัยได้อย่างไรหากไม่มีเลย


2
ฉันจะแทนที่ 'len (x)> 0' ด้วย 'x' ที่นี่
Ulf Aslak

1

บางทีนี่อาจจะทำงานได้ดีขึ้นเล็กน้อยในกรณีที่ข้อมูลอินพุตจำนวนมากไม่มีชิ้นส่วนที่คุณต้องการเนื่องจากมีค่าใช้จ่ายมากกว่า

def return_first_match(text):
    result = re.findall('\d+',text)
    result = result[0] if result else ""
    return result
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.