Как изменить мое регулярное выражение, чтобы оно соответствовало / заменялось на 2-м, 3-м, ... словах, но не на первом? - PullRequest
3 голосов
/ 09 октября 2019

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

Входные данные: текст (может состоять из букв латинского и кириллического алфавита, не содержит _)

Выходные данные: источниктекст, но перед всеми повторяющимися словами подчеркивание _

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

Метод для завершения:

public static String convert (String input) {
    ...
}

Пример ввода:

This is a test
And this is also a test
And these are also tests
test
Это тест
Это также тест
И это также тесты

Пример вывода:

This _is _a _test
_And this _is _also _a _test
_And these are _also tests
_test
_Это _тест
_Это _также _тест
И это _также тесты

Моя попытка:

public static void convert(String input) {
        Pattern p = Pattern.compile("(\\b\\w+\\b)(?=[\\s\\S]*\\b\\1\\b[\\s\\S]*\\b\\1\\b)", Pattern.UNICODE_CHARACTER_CLASS);
        String res = p.matcher(input+" "+input).replaceAll("_$1");
        res = res.substring(0, res.length() - 1 - p.matcher(input).replaceAll("_$1").length());
        System.out.println(res);
    }

Мой вывод: введите описание изображения здесь

This _is _a _test
_And this _is _also _a test
_And these are _also tests
_test
_Это _тест
_Это _также _тест
И это _также тесты

Слово "тест" во втором ряду без "_", номне нужно "_test"

1 Ответ

3 голосов
/ 09 октября 2019

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

// Java 9+
String s = "This is a test\nAnd this is also a test\nAnd these are also tests\ntest\nЭто тест\nЭто также тест\nИ это также тесты";
String rx = "(?sU)\\b(\\w+)\\b(?=.*\\b\\1\\b)";
String[] results = Pattern.compile(rx).matcher(s).results().map(MatchResult::group).toArray(String[]::new);
System.out.println(s.replaceAll("(?U)\\b(?:" + String.join("|", results) + ")\\b", "_$0"));

// Java 8
String s = "This is a test\nAnd this is also a test\nAnd these are also tests\ntest\nЭто тест\nЭто также тест\nИ это также тесты";
String rx = "(?sU)\\b(\\w+)\\b(?=.*\\b\\1\\b)";
List<String> matches = new ArrayList<>();
Matcher m = Pattern.compile(rx).matcher(s);
while (m.find()) {
    matches.add(m.group());
}
System.out.println(s.replaceAll("(?U)\\b(?:" + String.join("|", matches) + ")\\b", "_$0"));

См. Java-демонстрацию онлайн и демонстрацию второго фрагмента . Выходные данные:

This _is _a _test
_And this _is _also a _test
And these are _also tests
test
_Это _тест
_Это _также тест
И это _также тесты

Примечание. Я заменил обходную конструкцию [\s\S] на . в сочетании с опцией встроенного флага s DOTALL (чтобы . также мог соответствовать разрывам строк), использовал Java9+ .results() метод для возврата всех совпадений и построения окончательного шаблона из найденных совпадений, объединенных с помощью оператора | ИЛИ.

Подробности

  • (?sU)\b(\w+)\b(?=.*\b\1\b):
    • (?sU) - встроенный DOTALL (также делает . совпадение с переносом строки) и UNICODE_CHARACTER_CLASS (делает все короткие имена Unicode распознаваемыми) опции флага
    • \b - словограница
    • (\w+) - группа 1: 1+ слова, буквы, цифры или _ s
    • \b - граница слова
    • (?=.*\b\1\b) - немедленносправа должно быть любое число 0+, как можно больше, с последующим тем же значением, что и в группе 1, как целое слово.
  • (?U)\\b(?:" + String.join("|", results) + ")\\b": этот шаблон будетвыглядеть как (?U)\b(?:test|is|Это|тест|также)\b
    • (?U) - встроенная опция флага UNICODE_CHARACTER_CLASS
    • \b - граница слова
    • (?:test|is|Это|тест|также) - группа чередования без захвата
    • \b - граница слова

Замена_$0 для второго регулярного выражения, так как _ добавляется ко всему значению совпадения, $0.

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