Получить список подстрок из списка строк, где подстроки соответствуют определенному регулярному выражению

1

Этот вопрос относится к Python 3. 6+ (но не стесняйтесь отвечать за более низкие Pythons для других читателей).

Я хочу извлечь подстроку из каждой строки, которая соответствует регулярному выражению.

Скажем, у меня есть следующее:

a = ['v-01-001', 'v-01-002', 'v-02-001', 'v-02-002', 'v-02-003', 'v-03-001']

Я хочу, чтобы последние 3 цифры всех строк соответствовали v-02-\d\d\d, то есть:

['001', '002', '003']

Моя наивная попытка:

[x[1] for x in list(map(lambda i: re.search(r'v-02-(\d\d\d)', i), a)) if x]

Можете ли вы придумать что-нибудь более элегантное?

Спасибо

  • 2
    Требуется ли регулярное выражение?
  • 0
    Не более элегантно, но может быть проще читать: d = [elem.split('v-02-')[1] for elem in a if elem.startswith('v-02')]
Показать ещё 2 комментария
Теги:
list-comprehension

2 ответа

1
Лучший ответ

Вы могли бы сделать что-то вроде этого:

import re

a = ['v-01-001', 'v-01-002', 'v-02-001', 'v-02-002', 'v-02-003', 'v-03-001']
pattern = re.compile('v-02-(\d{3})$')
print([m.group(1) for m in map(pattern.match, a) if m])

Выход

['001', '002', '003']

Также вы можете использовать finditer:

print([m.group(1) for ms in map(pattern.finditer, a) for m in ms])

Выход

['001', '002', '003']
  • 0
    Ответ @ dawg очень исчерпывающий, но использование скомпилированного регулярного выражения, а затем просто передача его функции поиска на карту - идеальное решение - мне все равно приходится создавать регулярное выражение каждый раз, когда я его использую (первая часть меняется в тот же день, поэтому не могу сохранить в константе) и я не использовал функцию компиляции! Я сделал что-то вроде: [m[1] for m in map(pattern.match, a) if m]
1

Четыре способа сделать это.

Первый - это просто обычная петля ole:

li=[]
for s in a:
    m = re.search(r'v-02-(\d\d\d)', s)
    if m:
        li.append(m.group(1))
 # li=['001', '002', '003']

Второй в двух вызовах одного и того же регулярного выражения в понимании списка:

>>> [re.search(r'v-02-(\d\d\d)', s).group(1) for s in a if re.search(r'v-02-(\d\d\d)', s)]
['001', '002', '003']

В-третьих, использовать map:

>>> [m.group(1) for m in map(lambda s: re.search(r'v-02-(\d\d\d)', s), a) if m]
['001', '002', '003']

Наконец, вы можете сгладить список с помощью .join а затем использовать findall:

>>> re.findall(r'\bv-02-(\d\d\d)\b', '\t'.join(a))
['001', '002', '003']

Или, используйте \n и re.M против двух \b:

>>> re.findall(r'^v-02-(\d\d\d)$', '\n'.join(a), flags=re.M)
['001', '002', '003']

Я бы, наверное, написал это в том же порядке, если бы написал этот бит кода.

То, что считается более элегантным, в глазах смотрящего, я полагаю. Я считаю, что последний из них более изящный.


Вы также можете пропустить регулярное выражение и использовать строковые методы Python:

>>> prefix='v-02-'
>>> [e[len(prefix):] for e in filter(lambda s: s.startswith(prefix),a)]
['001', '002', '003']

Вероятно, это будет самым быстрым, если это имеет значение в этом случае.


В декабре 2019 года будет более элегантная альтернатива. Как определено в PEP 572, вы сможете использовать оператор присваивания, чтобы вы могли назначить совпадение и проверить соответствие за один шаг:

[m.group(1) for s in a if (m:=re.search(r'v-02-(\d\d\d)', s))]
  • 0
    Последний (PEP 572) - это то, что я пытался сделать изначально - провести матч, а затем прочитать матч в первой части списка понимания - так приятно знать, что это произойдет!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню