Регулярное выражение для последовательных повторяющихся слов
Я новичок в регулярных выражениях, и я не могу понять, как написать одно регулярное выражение, которое бы "соответствовало" любым повторяющимся последовательным словам, таким как:
Париж весной.
Не то что связано.
Почему Вы смеетесь? Мои регулярные выражения это плохо?
Есть ли одно регулярное выражение, которое будет соответствовать ВСЕМ жирным строкам выше?
18 ответов
Попробуйте это регулярное выражение:
\b(\w+)\s+\1\b
Вот \b
это граница слова и \1
ссылается на захваченный матч первой группы.
Я считаю, что это регулярное выражение обрабатывает больше ситуаций:
/(\b\S+\b)\s+\b\1\b/
Хороший выбор тестовых строк можно найти здесь: http://callumacrae.github.com/regex-tuesday/challenge1.html
Выражение ниже должно работать правильно, чтобы найти любое количество последовательных слов. Соответствие может быть без учета регистра.
String regex = "\\b(\\w+)(\\s+\\1\\b)*";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(input);
// Check for subsequences of input that match the compiled pattern
while (m.find()) {
input = input.replaceAll(m.group(0), m.group(1));
}
Пример ввода: до свидания до свидания GooDbYe
Пример вывода: до свидания
Объяснение:
Выражение регулярного выражения:
\ b: начало границы слова
\w+: любое количество символов слова
(\s+\1\b)*: любое количество пробелов, за которыми следует слово, совпадающее с предыдущим словом и заканчивающее границу слова. Вся вещь, завернутая в *, помогает найти более одного повторения.
Группировка:
m.group (0): должен содержать соответствующую группу в вышеприведенном случае. До свидания, до свидания, GooDbYe
m.group (1): должен содержать первое слово сопоставленного шаблона в вышеприведенном случае. До свидания
Метод Replace заменяет все последовательные совпадающие слова на первый экземпляр слова.
Попробуйте это ниже RE
- \ b начало слова граница слова
- \W+ любой символ слова
- \1 одно и то же слово уже найдено
- конец слова
()* Повторять снова
public static void main(String[] args) { String regex = "\\b(\\w+)(\\b\\W+\\b\\1\\b)*";// "/* Write a RegEx matching repeated words here. */"; Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE/* Insert the correct Pattern flag here.*/); Scanner in = new Scanner(System.in); int numSentences = Integer.parseInt(in.nextLine()); while (numSentences-- > 0) { String input = in.nextLine(); Matcher m = p.matcher(input); // Check for subsequences of input that match the compiled pattern while (m.find()) { input = input.replaceAll(m.group(0),m.group(1)); } // Prints the modified sentence. System.out.println(input); } in.close(); }
Regex to Strip 2+ повторяющиеся слова (последовательные / непоследовательные слова)
Попробуйте это регулярное выражение, которое может поймать 2 или более повторяющихся слова и оставить только одно слово. И дубликаты слов не обязательно должны быть последовательными.
/(\b\w+\b)(?=\b.*\1\b)/ig
Вот, \b
используется для границы слова, ?=
используется для позитивного взгляда, и \1
используется для обратной ссылки.
Широко используемая библиотека PCRE может справиться с такими ситуациями (однако вы не добьетесь того же с POSIX-совместимыми механизмами регулярных выражений):
(\b\w+\b)\W+\1
Вот тот, который ловит несколько слов несколько раз:
(\b\w+\b)(\s+\1)+
Это регулярное выражение, которое я использую, чтобы удалить повторяющиеся фразы в моем роботе:
(\S+\s*)\1{2,}
(\S+\s*)
ищет любую строку символов, которая не является пробелом, затем пробел.
\1{2,}
затем ищет более 2 экземпляров этой фразы в строке для сопоставления. Если есть 3 фразы, которые идентичны, это соответствует.
Это неправильная грамматика. Вы можете использовать регулярные выражения, специфичные для движка / языка, но вы не можете использовать универсальное регулярное выражение.
Поскольку некоторые разработчики заходят на эту страницу в поисках решения, которое не только устраняет дубликаты последовательных непробельных подстрок, но и повторяет их и так далее, я покажу адаптированный шаблон.
Шаблон: /(\b\S+)(?:\s+\1\b)+/
( Образец Демо)
Заменить: $1
(заменяет совпадение полной строки с группой захвата #1)
Этот шаблон жадно сопоставляет "целую" подстроку без пробелов, затем требуется одна или несколько копий совпадающей подстроки, которая может быть разделена одним или несколькими пробельными символами (пробел, табуляция, символ новой строки и т. Д.).
В частности:
\b
(граница слова) символы жизненно важны, чтобы гарантировать, что отдельные слова не совпадают.- Вторая скобка - это группа без захвата, потому что эту подстроку переменной ширины не нужно захватывать - только сопоставлять / поглощать.
+
(один или несколько квантификаторов) в группе без захвата является более подходящим, чем*
так как*
будет "беспокоить" движок регулярных выражений для захвата и замены одноэлементных вхождений - это расточительный дизайн паттернов.
* обратите внимание, если вы имеете дело с предложениями или строками ввода с пунктуацией, шаблон необходимо будет доработать.
Пример в Javascript: Хорошие части могут быть адаптированы для этого:
var doubled_words = /([A-Za-z\u00C0-\u1FFF\u2800-\uFFFD]+)\s+\1(?:\s|$)/gi;
\ b использует \w для границ слова, где \w эквивалентно [0-9A-Z_a-z]. Если вы не возражаете против этого ограничения, принятый ответ - хорошо.
Вы можете использовать этот шаблон:
\b(\w+)(?:\W+\1\b)+
Этот шаблон можно использовать для сопоставления всех повторяющихся групп слов в предложениях. :)
Вот пример функции util, написанной на Java 17, которая заменяет все дубликаты первым вхождением:
public String removeDuplicates(String input) {
var regex = "\\b(\\w+)(?:\\W+\\1\\b)+";
var pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
var matcher = pattern.matcher(input);
while (matcher.find()) {
input = input.replaceAll(matcher.group(), matcher.group(1));
}
return input;
}
Это выражение (навеянное Майком, выше), кажется, улавливает все дубликаты, тройки и т. Д., Включая те, что находятся в конце строки, чего большинство других не делают:
/(^|\s+)(\S+)(($|\s+)\2)+/g, "$1$2")
Я знаю вопрос, заданный для совпадения только с дубликатами, но три дубликата - это всего лишь 2 дубликата рядом друг с другом:)
Сначала я положил (^|\s+)
чтобы убедиться, что он начинается с полного слова, в противном случае "детский стейк" перейдет к "детский стейк" ("s" будет соответствовать). Затем он соответствует всем полным словам ((\b\S+\b)
), за которым следует конец строки ($
) или количество пробелов (\s+
), все повторяется более одного раза.
Я попробовал это так, и это сработало хорошо:
var s = "here here here here is ahi-ahi ahi-ahi ahi-ahi joe's joe's joe's joe's joe's the result result result";
print( s.replace( /(\b\S+\b)(($|\s+)\1)+/g, "$1"))
--> here is ahi-ahi joe's the result
Чтобы найти повторяющиеся слова, которые не имеют начального или конечного символа (символов) без пробелов, кроме символа (символов) слова, вы можете использовать границы пробелов слева и справа, используя
Шаблон будет иметь совпадение в:
Париж в
the the
весна.Нет
that that
относится.
Шаблон не будет иметь соответствия в:
- Это $word слово
(?<!\S)(\w+)\s+\1(?!\S)
Объяснение
-
(?<!\S)
Отрицательный взгляд назад, не утверждается непробельный символ слева от текущего местоположения -
(\w+)
Захватите группу 1, сопоставьте 1 или более словесных символов -
\s+
Соответствует 1 или более пробельным символам (обратите внимание, что это также может соответствовать новой строке) -
\1
Обратная ссылка для соответствия такой же, как в группе 1 -
(?!\S)
Отрицательный взгляд вперед, утверждать, что справа от текущего местоположения нет символа, отличного от пробела.
См.
Чтобы найти 2 или более повторяющихся слов:
(?<!\S)(\w+)(?:\s+\1)+(?!\S)
- Эта часть узора
(?:\s+\1)+
использует группу без захвата для повторения 1 или более раз, совпадающего с 1 или более символами пробела, за которым следует обратная ссылка, чтобы соответствовать так же, как в группе 1.
См.
Альтернативы без использования обходных путей
Вы также можете использовать начальное и конечное чередование, соответствующее либо символу пробела, либо утверждать начало/конец строки.
Затем используйте группу захвата 1 для значения, которое вы хотите получить, и используйте вторую группу захвата с обратной ссылкой\2
соответствовать повторяющемуся слову.
Соответствие 2 повторяющимся словам:
(?:\s|^)((\w+)\s+\2)(?:\s|$)
См.
Соответствие 2 или более повторяющимся словам:
(?:\s|^)((\w+)(?:\s+\2)+)(?:\s|$)
См.
Попробуйте это регулярное выражение, которое подходит для всех случаев повторяющихся слов:
\b(\w+)\s+\1(?:\s+\1)*\b
Я думаю, что другим решением было бы использовать именованные группы захвата и обратные ссылки , подобные этому:
.* (?<mytoken>\w+)\s+\k<mytoken> .*/ OR .*(?<mytoken>\w{3,}).+\k<mytoken>.*/
Котлин:
val regex = Regex(""".* (?<myToken>\w+)\s+\k<myToken> .*""")
val input = "This is a test test data"
val result = regex.find(input)
println(result!!.groups["myToken"]!!.value)
Ява:
var pattern = Pattern.compile(".* (?<myToken>\\w+)\\s+\\k<myToken> .*");
var matcher = pattern.matcher("This is a test test data");
var isFound = matcher.find();
var result = matcher.group("myToken");
System.out.println(result);
JavaScript:
const regex = /.* (?<myToken>\w+)\s+\k<myToken> .*/;
const input = "This is a test test data";
const result = regex.exec(input);
console.log(result.groups.myToken);
// OR
const regex = /.* (?<myToken>\w+)\s+\k<myToken> .*/g;
const input = "This is a test test data";
const result = [...input.matchAll(regex)];
console.log(result[0].groups.myToken);
Все вышеперечисленное обнаруживает
test
как повторяющееся слово.
Протестировано с Kotlin 1.7.0-Beta , Java 11 , Chrome и Firefox 100 .
Насколько я вижу, ни один из них не будет совпадать: Лондон зимой (с зимой на новой строке) Хотя сопоставление дубликатов в одной строке довольно просто, я не смог найти решение для ситуации, в которой они растягиваются на две строки. (с Перлом)
Используйте это в случае, если вы хотите, чтобы проверка на наличие повторяющихся слов производилась без учета регистра.
(?i)\\b(\\w+)\\s+\\1\\b