replaceAll для StringBuilder с поддержкой регулярных выражений?

Я просмотрел Java API и некоторые распространенные сторонние библиотеки, но не могу найти подходящий метод, который будет делать то, что делает String.replaceAll, за исключением StringBuilder.

Я знаю, что с небольшими усилиями это можно сделать и для StringBuffer, но я не хочу идти по этому пути, потому что StringBuffer медленнее.

Кто-нибудь знает о каких-либо сторонних утилитах или есть ли быстрый фрагмент кода для реализации этой функции?


person Jin Kim    schedule 28.06.2013    source источник
comment
Является ли критическая производительность или такая большая строка, что преобразование ее в строку и выполнение замены (и преобразование ее обратно, если требуется) не может быть и речи?   -  person Bernhard Barker    schedule 28.06.2013
comment
Вы можете создать Matcher (который можно создать на любом CharSequence), а затем написать цикл с appendReplacement и appendTail, чтобы написать свой собственный replaceAll.   -  person Martin Ender    schedule 28.06.2013
comment
Да, но тот использует StringBuffer...   -  person Audrius Meskauskas    schedule 28.06.2013
comment
На самом деле, вам даже не нужна вся эта append* магия... Matcher предоставляет свои собственные replaceAll.   -  person Martin Ender    schedule 28.06.2013
comment
@AudriusMeškauskas Теперь я понимаю, что ты имеешь в виду. Функции append* также принимают StringBuffer... достаточно честно.   -  person Martin Ender    schedule 28.06.2013


Ответы (4)


String.replaceAll — это просто удобный метод для Matcher.replaceAll. Matcher - это "фактический" способ использования регулярных выражений в Java, который допускает гораздо более сложные варианты использования.

Более того, все, что можно сделать с помощью методов регулярных выражений на String, можно сделать с помощью аналогичных методов на Matcher. Прелесть в том, что Matcher работают не только с String: Matcher можно получить для любого CharSequence (интерфейс, реализованный StringBuilder, StringBuffer, String и CharBuffer). Итак, вы можете просто сделать:

import java.util.regex.*;

...

StringBuilder sb = new StringBuilder();
sb.append("This works with StringBuffers");
Pattern p = Pattern.compile("\\Buffer\\B");
Matcher m = p.matcher(sb);
System.out.println(m.replaceAll("uilder"));

Выведет This works with StringBuilders.

Рабочая демонстрация.

person Martin Ender    schedule 28.06.2013
comment
Я не уверен, что OP предназначен для чего-то подобного, поскольку m.relaceAll будет внутренне использовать StringBuffer и возвращать String с замененными данными. Я полагаю, что OP хочет знать, есть ли способ использовать StringBuilder вместо StringBuffer, как мы используем его в Matcher#appendReplacement(), но я могу ошибаться. Комментарий ОП был бы лучше здесь. - person Pshemo; 28.06.2013
comment
@Pshemo ах, ладно, я не знал, что Matcher использует StringBuffer внутри. - person Martin Ender; 28.06.2013
comment
В основном у меня есть следующая подпись метода: public void replaceAll(final StringBuilder builder, final String regex, final String replacement). Данные построителя должны быть изменены без повторного назначения. - person Jin Kim; 06.07.2013

Regex не изменяет изменяемый CharSequence внутри. Regex анализирует CharSequence, чтобы вернуть String, где String — результат. StringBuffer является исключением, так как существует особая обработка - поскольку StringBuilder является CharSequence, вы должны изменить его с результатом совпадения.

Что вы можете сделать вместо этого:

// Class
private static final Pattern MY_PATTERN = Pattern.compile("my|regex");

{ // Method
    StringBuilder builder;
    // ...

    Matcher m = MY_PATTERN.matcher(builder);
    builder.replace(0, builder.length(), m.replaceAll("<b>$0</b>"));
}

Посмотрите демонстрацию тестового кода!

person Unihedron    schedule 12.07.2014

Я не хочу идти по этому пути, потому что StringBuffer медленнее.

Верно, но с обычной оговоркой о преждевременной оптимизации и, что более важно, современные JVM используют escape-анализ для удаления StringBuffer. /Vector/HashTable блокируется в некоторых случаях, поэтому после оптимизации производительность будет примерно такой же.

person David Ehrmann    schedule 12.07.2014

Apache Harmony Исходный код Matcher кажется полностью переработанным для использования с StringBuilder вместо используемого в настоящее время StringBuffer, просто перейдите в другой пакет. Вроде не таскает за собой много зависимостей. Лицензия Apache, которая находится в начале файла, может быть неплохой даже для коммерческого проекта.

Код GNU Classpath также можно использовать повторно, но лицензия там сложнее (вам нужно опубликовать измененную версию Matcher, но, вероятно, не остальной код). То же самое касается оригинальной реализации Sun, которую можно найти здесь в проекте OpenJDK.

person Audrius Meskauskas    schedule 28.06.2013