Регулярное выражение для последовательных повторяющихся биграмм

Мой вопрос является прямым продолжением этого более раннего вопроса об обнаружении последовательных слов (униграмм) в строке.

В предыдущем вопросе

Не то что связано

может быть обнаружено с помощью этого регулярного выражения: \b(\w+)\s+\1\b

Здесь я хочу обнаружить последовательные биграммы (пары слов):

синие и то и то очень яркие

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

синие и очень яркие

(для этого приложения, если это имеет значение, я использую gsub в R)

2 ответа

Решение

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

(\b.+\b)\1\b

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

(\b.+?\b)\1\b

Смотрите это регулярное выражение. Строка замены будет \1 - обратная ссылка на захваченную часть, сначала сопоставленную с конструкцией группировки (...),

Вам нужно регулярное выражение PCRE, чтобы оно работало, поскольку есть задокументированные проблемы с соответствием границ нескольких слов с gsub (так, добавьте perl=T аргумент).

Режим POSIX 1003.2 для gsub и gregexpr не работает правильно с повторяющимися границами слов (например, pattern = "\b"). использование perl = TRUE для таких совпадений (но это может не работать должным образом с входными данными, не относящимися к ASCII, поскольку значение слова зависит от системы).

Обратите внимание, что если ваши повторяющиеся подстроки могут занимать несколько строк, вы можете использовать регулярное выражение PCRE с модификатором DOTALL (?s) в начале шаблона (так что . может также соответствовать символу новой строки).

Итак, код R будет выглядеть

gsub("(?s)(\\b.+\\b)\\1\\b", "\\1", s, perl=T)

или же

gsub("(?s)(\\b.+?\\b)\\1\\b", "\\1", s, perl=T)

Смотрите демоверсию IDEONE:

text <- "are blue and then and then more and then and then more very bright"
gsub("(?s)(\\b.+?\\b)\\1\\b", "\\1", text, perl=T) ## shorter repeated substrings
## [1] "are blue and then more and then more very bright"
gsub("(?s)(\\b.+\\b)\\1\\b", "\\1", text, perl=T) ## longer repeated substrings
## [1] "are blue and then and then more very bright"

Попробуйте следующий RegEx:

(\b.+?\b)\1\b

RegEx захватывает границу слова, затем данные и затем границу слова. \1 будет ссылаться на то, что было захвачено, и выберите это снова. Затем он проверит на границу слова конец, чтобы предотвратить a and а также z zoo из выбранных

Что касается замены, используйте \1, Это будет содержать данные из 1stCapture Group (первая часть биграммы), и эта первая часть будет использоваться для замены всего этого.

Живая Демо на Regex101

Другие вопросы по тегам