Давайте итеративно разработаем решение; на каждом этапе мы указываем, в чем заключаются проблемы, и исправляем их, пока не дойдем до окончательного ответа.
Мы можем начать с чего-то вроде этого:
String s = "What???? Impo$$ible!!!";
String v = "!";
s = s.replaceAll(v + "{2,}", v);
System.out.println(s);
// "What???? Impo$$ible!"
{2,}
- это синтаксис регулярных выражений для конечного повторения, означающий «по крайней мере 2 из» в этом случае.
Просто так вышесказанное работает, потому что !
не является метасимволом регулярных выражений. Давайте посмотрим, что произойдет, если мы попробуем следующее:
String v = "?";
s = s.replaceAll(v + "{2,}", v);
// Exception in thread "main" java.util.regex.PatternSyntaxException:
// Dangling meta character '?'
Одним из способов решения проблемы является использование Pattern.quote
, так что v
воспринимается буквально:
s = s.replaceAll(Pattern.quote(v) + "{2,}", v);
System.out.println(s);
// "What? Impo$$ible!!!"
Оказывается, это не единственное, о чем нам нужно беспокоиться: в замещающих строках \
и $
также являются специальными метасимволами. Это объясняет, почему мы получаем следующую проблему:
String v = "$";
s = s.replaceAll(Pattern.quote(v) + "{2,}", v);
// Exception in thread "main" java.lang.StringIndexOutOfBoundsException:
// String index out of range: 1
Поскольку мы хотим, чтобы v
воспринималось буквально как строка замены, мы используем Matcher.quoteReplacement
следующим образом:
s = s.replaceAll(Pattern.quote(v) + "{2,}", Matcher.quoteReplacement(v));
System.out.println(s);
// "What???? Impo$ible!!!"
Наконец, повторение имеет более высокий приоритет, чем конкатенация. Это означает следующее:
System.out.println( "hahaha".matches("ha{3}") ); // false
System.out.println( "haaa".matches("ha{3}") ); // true
System.out.println( "hahaha".matches("(ha){3}") ); // true
То есть, если v
может содержать несколько символов, вам нужно сгруппировать его перед применением повторения. В этом случае вы можете использовать группу без захвата, поскольку вам не нужно создавать обратную ссылку.
String s = "well, well, well, look who's here...";
String v = "well, ";
s = s.replaceAll("(?:" +Pattern.quote(v)+ "){2,}", Matcher.quoteReplacement(v));
System.out.println(s);
// "well, look who's here..."
Основная информация
- Для сопоставления произвольной литеральной строки, которая может содержать метасимволы регулярных выражений, используйте
Pattern.quote
- Чтобы заменить произвольной литеральной строкой, которая может содержать метасимволы замены, используйте
Matcher.quoteReplacement
Ссылки
Бонусный материал
В следующем примере используются повторение с неохотой, группа захвата и обратные ссылки, смешанные с учетом без учета регистра:
System.out.println(
"omgomgOMGOMG???? Yes we can! YES WE CAN! GOAAALLLL!!!!"
.replaceAll("(?i)(.+?)\\1+", "$1")
);
// "omg? Yes we can! GOAL!"
Смежные вопросы
Ссылки