Найти последнее совпадение с регулярным выражением python - PullRequest
22 голосов
/ 10 мая 2010

Я хочу соответствовать последнему появлению простого шаблона в строке, например,

list = re.findall(r"\w+ AAAA \w+", "foo bar AAAA foo2 AAAA bar2")
print "last match: ", list[len(list)-1]

однако, если длина строки очень , генерируется огромный список совпадений. Есть ли более прямой способ сопоставления со вторым появлением «AAAA» или я должен использовать этот обходной путь?

Ответы [ 5 ]

27 голосов
/ 10 мая 2010

вы можете использовать $, который обозначает символ конца строки:

>>> s = """foo bar AAAA
foo2 AAAA bar2"""
>>> re.findall(r"\w+ AAAA \w+$", s)
['foo2 AAAA bar2']

Также обратите внимание, что list - это недопустимое имя для вашей переменной, поскольку оно скрывает встроенный тип. Чтобы получить доступ к последнему элементу списка, вы можете просто использовать [-1] index:

>>> lst = [2, 3, 4]
>>> lst[-1]
4
23 голосов
/ 07 июня 2010

Вы можете избежать построения списка, просто перебирая все совпадения и сохраняя последнее совпадение:

for match in re.finditer(r"\w+ AAAA \w+", "foo bar AAAA foo2 AAAA bar2"):
    pass

После этого match содержит последнее совпадение и работает для всех комбинаций pattern и искомая строка . Возможно, вы захотите сначала установить match на None, поскольку, если совпадений нет, match не будет иметь никакого значения.

2 голосов
/ 23 мая 2018

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

import re
p = r"AAAA(?=\s\w+)" #revised per comment from @Jerry
p2 =r"\w+ AAAA \w+"
s = "foo bar AAAA foo2 AAAA bar2"
l = re.findall(p, s)
l2 = re.findall(p2, s)
print('l: {l}'.format(l=l))

#print(f'l: {l}') is nicer, but online interpreters sometimes don't support it.
# https://www.onlinegdb.com/online_python_interpreter
#I'm using Python 3.

print('l2: {l}'.format(l=l2))
for m in re.finditer(p, s):
  print(m.span())
  #A span of (n,m) would really represent characters n to m-1 with zero based index
  #So.(8,12):
  # => (8,11: 0 based index)
  # => (9th to 12th characters conventional 1 based index)
print(re.findall(p, s)[-1])

Выходы:

l: ['AAAA', 'AAAA']
l2: ['bar AAAA foo2']
(8, 12)
(18, 22)   
AAAA

Причиной получения двух результатов вместо одного в оригинале является специальный соус (?=).

Это называется позитивный взгляд. Он не ' потребляет ' (то есть перемещает курсор), когда совпадение найдено во время вычисления регулярного выражения. Итак, он возвращается после сопоставления.

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

Таким образом, хотя шаблон сопоставляется, результаты опускают окружающую последовательность буквенно-цифровых символов, представленных \w+ и промежуточными пробелами, \s в моем примере -representing [ \t\n\r\f\v]. (Подробнее здесь )

Так что я получаю только AAAA каждый раз.

p2 здесь, представляет исходный шаблон кода @SDD, человека, задающего вопрос.

foo2 - это , потребляемый , с этим шаблоном, чтобы второй AAAA не совпадал, так как курсор продвинулся слишком далеко, когда механизм регулярных выражений возобновляет свою вторую итерацию соответствия.


Я рекомендую взглянуть на видео Moondra на Youtube, если вы хотите углубиться.

Он сделал очень тщательную серию из 17 частей на Python Regex, начиная с здесь


Вот ссылка на онлайн переводчика Python

1 голос
/ 20 января 2019

Нет встроенной библиотеки re, которая поддерживает разбор строк справа налево, во входной строке выполняется поиск только шаблона слева направо.

Однако существует модуль регулярных выражений PyPi , который поддерживает эту функцию. Это флаг regex.REVERSE или его встроенный вариант, (?r):

s="foo bar AAAA foo2 AAAA bar2"
print(regex.search(r"(?r)\w+ AAAA \w+$", s).group())
# => foo2 AAAA bar2

С модулем re есть способ быстро добраться до конца строки, используя конструкцию ^[\s\S]*, и позволить обратному отслеживанию найти шаблон, который вы хотели бы объединить в отдельную группу. Однако обратный трекинг может поглотить часть совпадения (так как он перестанет давать больше текста после совпадения всех последующих шаблонов), и в случае, если текст слишком велик и совпадения нет, обратный трекинг может стать катастрофическим. Используйте этот трюк только в том случае, если ваша входная строка всегда совпадает или если она короткая, а пользовательский шаблон не полагается на обратный возврат:

print(re.search(r"(?:^[\s\S]*\W)?(\w+ AAAA \w+)$", s).group(1))
# => foo2 AAAA bar2

Здесь (?:^[\s\S]*\W)? соответствует необязательной последовательности начала строки, любые 0 или более символов сопровождаются символом, не состоящим из слова (\W). Необходимо добавить \W, чтобы возврат вернулся к несловому символу, и это необязательно, так как совпадение может начаться в начале строки.

См. Демоверсию Python .

0 голосов
/ 16 января 2019

Другой быстрый способ - использование search и group:

>>> re.search('\w+ AAAA \w+$',"foo bar AAAA foo2 AAAA bar2").group(0)
'foo2 AAAA bar2'

Что он делает:

  1. Используется шаблон \w+ AAAA \w+$, который получает последнее вхождение 'AAAA' с рядом стоящих слов, все из которых используют \w+ (дважды) и $ (один раз) .

  2. После процесса сопоставления с образцом вам придется использовать метод _sre.SRE_Match.<strong>group</strong>, чтобы получить значение принадлежности объекта _sre.SRE_Match и, конечно же, получить нулевую (первую) группу, поскольку известно, что search сохраняет только один матч (ноль).

Вот регулярное выражение этого.

Вот время для всех ответов (кроме ответа JGFMK, так как это сложно):

>>> timeit.timeit(lambda: re.findall(r"\w+ AAAA \w+$", s),number=1000000) # SilentGhost
5.783595023876842
>>> timeit.timeit('import re\nfor match in re.finditer(r"\w+ AAAA \w+", "foo bar AAAA foo2 AAAA bar2"):pass',number=1000000) # tzot
5.329235373691631
>>> timeit.timeit(lambda: re.search('\w+ AAAA \w+$',"foo bar AAAA foo2 AAAA bar2").group(0),number=1000000) # mine (U9-Forward)
5.441731174121287
>>> 

Я проверяю все тайминги с помощью модуля timeit, а также я делаю number=1000000, так что это занимает гораздо больше времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...