Подтвердить что ты не робот

Найти последний матч с регулярным выражением python

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

Ответ 3

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

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 в моем примере - представляющими [ \t\n\r\f\v]. (Подробнее здесь)

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

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

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


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

Он сделал очень тщательную серию из 17 частей по регулярным выражениям Python, начиная здесь


Здесь ссылка на онлайн переводчик Python.

Ответ 4

Нет встроенной функции 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 или более символов, за которыми следует не слово char (\W). Необходимо добавить \W чтобы возврат к возврату не состоял из слова char, и он должен быть необязательным, так как совпадение может начинаться в начале строки.

Посмотрите демо Python.

Ответ 5

Еще один быстрый способ использования search и group:

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

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

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

  2. После процесса сопоставления с образцом вам нужно будет использовать _sre.SRE_Match. group _sre.SRE_Match. group метод для получения значения принадлежности объекта _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 модуля timeit, а также делаю number=1000000 поэтому это занимает гораздо больше времени.