повернути рядок із першим відповідним регулярним виразом


90

Я хочу отримати перший збіг регулярного виразу.

У цьому випадку я отримав список:

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замість цього перевірку.
Вішаль

Відповіді:


104

Ви можете вставити за ''замовчуванням у свій регулярний вираз, додавши |$:

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

Чудово, чи має пошук / .group якусь перевагу над findall / [0]?
Луїс Рамон Рамірес Родрігес

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

12

Я б пішов із:

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

re.searchу будь-якому випадку шукає лише перший збіг у рядку, тому, я думаю, це робить ваш намір дещо зрозумілішим, ніж використання findall.


7

Ви взагалі не повинні використовувати .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' тут.
Ульф Аслак,

1

Можливо, це могло б зробити трохи краще, якщо більший обсяг вхідних даних не містить бажаного фрагмента, оскільки крім того, він має більшу вартість.

def return_first_match(text):
    result = re.findall('\d+',text)
    result = result[0] if result else ""
    return result
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.