Ошибка шаблона фиксированной ширины регулярного выражения Python look-behind при поиске последовательных повторяющихся слов

У меня есть текст со словами, разделенными ., с экземплярами 2 и 3 последовательных повторяющихся слов:

My.name.name.is.Inigo.Montoya.You.killed.my.father.father.father.Prepare.to.die-

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

Так как есть макс. 3 последовательных повторяющихся слова, это

r'\b(\w+)\.+\1\.+\1\b'

successfully catches

father.father.father

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

r'\b(\w+)\.+\1(?!\.+\1)\b'

но мои попытки негативной оценки

r'(?<!(\w)\.)\b\1\.+\1\b(?!\.\1)'

либо вернуть проблему с фиксированной шириной (когда я сохраняю +), либо какую-то другую проблему.

Как мне исправить негативную ретроспективу?


person nacho    schedule 26.07.2017    source источник
comment
Как вы уже сказали, вы не можете использовать просмотр назад, если совпадение не имеет постоянной длины. Вы можете использовать библиотеку regex, которая является альтернативной библиотекой регулярных выражений, позволяющей смотреть назад.   -  person BrenBarn    schedule 26.07.2017
comment
Можно ли разделить части более чем на 1 точку? Вы использовали \.+, это предназначено? Посмотрите эту демонстрацию. Вы ищете что-то подобное?   -  person Wiktor Stribiżew    schedule 26.07.2017
comment
Если речь идет только об идентификации дубликатов из мультикаталогов \b(\w+)(?:\.(\1)(?:\.\1)+|\.(\1))\b Совпадения в группе 3 являются дубликатами, совпадения в группа 2 - это три или более захваченных слов.   -  person bobble bubble    schedule 26.07.2017


Ответы (2)


Может быть, регулярные выражения вообще не нужны.

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

  • группировать по словам (после разделения по точкам)
  • преобразовать в список и выдать значение tuple, считать, только если длина> 1

как это:

import itertools

s = "My.name.name.is.Inigo.Montoya.You.killed.my.father.father.father.Prepare.to.die"

matches = [(l[0],len(l)) for l in (list(v) for k,v in itertools.groupby(s.split("."))) if len(l)>1]

результат:

[('name', 2), ('father', 3)]

Таким образом, в основном мы можем делать с этим списком кортежей все, что захотим (например, фильтруя его по количеству вхождений)

Бонус (поскольку я сначала неправильно понял вопрос, поэтому я оставляю его): удалить дубликаты из предложения - сгруппировать по словам (после разделения по точкам), как указано выше, - взять только ключ (значение) возвращаемых значений в списке comp (нам не нужны значения, так как мы не считаем) - соединиться с точкой

В одной строке (по-прежнему используется itertools):

new_s = ".".join([k for k,_ in itertools.groupby(s.split("."))])

результат:

My.name.is.Inigo.Montoya.You.killed.my.father.Prepare.to.die
person Jean-François Fabre    schedule 26.07.2017
comment
Можно потерять next, я думаю, k for k,_ тоже должно работать, потому что ключ является каждым значением. - person DSM; 26.07.2017
comment
Спасибо! Мне не нужно удалять дубликаты, просто чтобы идентифицировать их, но подход кортежа должен работать. Я попробую! - person nacho; 26.07.2017
comment
@Jean-FrançoisFabre Это оказалось самым простым решением, поэтому в этом случае я отказался от регулярных выражений. Спасибо еще раз! - person nacho; 26.07.2017
comment
вы мудрый человек. Сложные регулярные выражения иногда очень сложно поддерживать/модифицировать. В таких простых случаях они на самом деле не нужны (ну, когда вы используете python!) - person Jean-François Fabre; 26.07.2017
comment
Ах, старое «регулярное выражение глупо, когда у вас есть аргумент разделения строки». matches = [(l[0],len(l)) for l in (list(v) for k,v in itertools.groupby(s.split("."))) if len(l)>1] легче, чем \b((\w+)\.+\2\.+\2?)\b?! Regex также является кроссплатформенным. Кроме того, быстрый тест: 10000 циклов, лучший из 3: 4,04 мкс на цикл | 10000 циклов, лучший из 3: 20,4 мкс на цикл - угадайте, какой из них использует регулярное выражение. - person joaoricardo000; 27.07.2017
comment
@joaoricardo000 ты используешь глупость, а не я. Позвольте мне перефразировать: регулярные выражения очень полезны, но иногда их сложно создавать/обслуживать, это тоже важный аспект. Если производительность имеет значение, то почему бы тогда не кодировать все на ассемблере? Кроме того: регулярное выражение является кросс-платформенным, но синтаксис различается (sed != awk!= python). Python является кроссплатформенным. спасибо за бенчмарк, интересно. - person Jean-François Fabre; 28.07.2017

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

r = re.compile(r'\b((\w+)\.+\2\.+\2?)\b')
r.findall(t)

> [('name.name.', 'name'), ('father.father.father', 'father')]

Просто сделайте третий повтор необязательным.


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

r = re.compile(r'\b((\w+)(\.+\2)\3*)\b')
r.findall(t)
> [('name.name', 'name', '.name'), ('father.father.father', 'father', '.father')]
person joaoricardo000    schedule 26.07.2017
comment
Для отказа от сопоставления конечной точки вы можете поставить вторую точку в незахватывающей группе. r'((\w+)\.+\2(?:\.+\2)?)'. - person kasravnd; 26.07.2017
comment
Если я не пробовал их неправильно, эти регулярные выражения, кажется, соответствуют как дубликатам, так и тройкам. Я пытаюсь найти разные регулярные выражения для сопоставления дубликатов и троек по отдельности. - person nacho; 26.07.2017