2010-08-12 3 views
61

Я что-то упускаю, или у StringBuilder нет такой же «замены всех вхождений строки A со строкой B», которую выполняет обычный класс String? Функция замены StringBuilder не совсем то же самое. Есть ли способ для этого более эффективно, не генерируя несколько строк, используя обычный класс String?Заменить все вхождения строки с помощью StringBuilder?

+0

http://download.oracle.com/javase/1.5.0/docs/api/java /lang/StringBuilder.html Я не знаю, если я что-то упустил, но эта функция, похоже, не существует. – garsh0p

+0

Что заставляет вас думать, что распределение объектов - это менее эффективный способ сделать это? В зависимости от того, сколько символов нужно копировать на каждом шаге замены «на месте», он мог бы просто выполнить операцию на месте. – Dirk

+1

'String.replaceAll' вещь с * regexs *? Я не стал бы беспокоиться о накладных расходах на конвертацию между 'StringBuilder' и' String'. –

ответ

67

Ну, вы можете написать цикл:

public static void replaceAll(StringBuilder builder, String from, String to) 
{ 
    int index = builder.indexOf(from); 
    while (index != -1) 
    { 
     builder.replace(index, index + from.length(), to); 
     index += to.length(); // Move to the end of the replacement 
     index = builder.indexOf(from, index); 
    } 
} 

Обратите внимание, что в некоторых случаях это может быть быстрее использовать lastIndexOf, работая со спины. Я подозреваю, что это так, если вы заменяете длинную строку коротким - поэтому, когда вы дойдете до начала, любые замены имеют меньше копий. В любом случае, это должно дать вам отправную точку.

+1

Обратите внимание, что в случае, если 'from' и' to' имеют разную длину, это решение будет перемещать буферный хвост на каждой замене. Это может быть весьма неэффективным для длинного буфера с большим количеством замещений. Ответ Рона Ромеро не имеет этого недостатка, но включает в себя поиск по одному регулярному выражению. Думаю, что будет быстрее, зависит от варианта использования. – Vadzim

2

java.util.regex.Pattern.matcher(CharSequence s) может использовать StringBuilder в качестве аргумента, так что вы можете найти и заменить каждое вхождение вашего шаблона используя начало() и конец() без вызова builder.toString()

31

Вы можете использовать Pattern/Matcher. Из сличитель Javadocs:

Pattern p = Pattern.compile("cat"); 
Matcher m = p.matcher("one cat two cats in the yard"); 
StringBuffer sb = new StringBuffer(); 
while (m.find()) { 
    m.appendReplacement(sb, "dog"); 
} 
m.appendTail(sb); 
System.out.println(sb.toString()); 
+0

это именно то, что я искал. Tnx! – dierre

+5

Это почти то же самое, что и Matcher # replaceAll(). – Shannon

+0

Это работает для «собаки», но недостаточно в случае «общего», потому что строка замены имеет специальные символы. Если вы собираетесь использовать обратные ссылки в значениях замены, тогда вам нужно избежать всех _other_ обратных слэшей и '$'. Если вам вообще не нужно ссылаться на согласованную строку, вы можете просто запустить заменяющий текст с помощью 'Matcher.quoteReplacement (...)'. Итак, 'm.appendReplacement (sb, Matcher.quoteReplacement (someText));' – AndrewF

11

Посмотрите на JavaDoc из replaceAll метод Строка Класс:

Заменяет каждую подстроку данной строки, которая соответствует заданному регулярному выражение с данной заменой. Вызов этого метода вида str.replaceAll (регулярное выражение, REPL) дает точно такой же результат как выражение

java.util.regex.Pattern.compile (регулярное выражение) .matcher (ул) .replaceAll (REPL)

Как вы можете видеть, вы можете использовать Pattern и Искателя, чтобы сделать это.

2

Используйте следующее:

/** 
* Utility method to replace the string from StringBuilder. 
* @param sb   the StringBuilder object. 
* @param toReplace the String that should be replaced. 
* @param replacement the String that has to be replaced by. 
* 
*/ 
public static void replaceString(StringBuilder sb, 
           String toReplace, 
           String replacement) {  
    int index = -1; 
    while ((index = sb.lastIndexOf(toReplace)) != -1) { 
     sb.replace(index, index + toReplace.length(), replacement); 
    } 
} 
4

Даже просто один использует строку ReplaceAll сама функция. Вы можете написать это как

StringBuilder sb = new StringBuilder("Hi there, are you there?") 
System.out.println(Pattern.compile("there").matcher(sb).replaceAll("niru")); 
1

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

public static void replaceAll(StringBuilder sb, Pattern pattern, String replacement) { 
    Matcher m = pattern.matcher(sb); 
    while(m.find()) { 
     sb.replace(m.start(), m.end(), replacement); 
    } 
} 

Я был в шоке, как простой код, чтобы сделать это (по какой-то причине, я думал, что изменение StringBuilder, используя сличитель бы бросить групповой старт/конца, но это не делает).

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

9

@Adam: Я думаю, что в вашем фрагменте кода вы должны отслеживать начальную позицию для m.find(), поскольку замена строки может изменить смещение после последнего совпадающего символа.

public static void replaceAll(StringBuilder sb, Pattern pattern, String replacement) { 
    Matcher m = pattern.matcher(sb); 
    int start = 0; 
    while (m.find(start)) { 
     sb.replace(m.start(), m.end(), replacement); 
     start = m.start() + replacement.length(); 
    } 
} 
+0

Я думаю, что вы правы. Я должен проверить, что у меня получилось. –

+1

Самый эффективный код памяти. Congrats! –

0
public static String replaceCharsNew(String replaceStr,Map<String,String> replaceStrMap){ 
     StringBuilder replaceStrBuilder = new StringBuilder(replaceStr); 
     Set<String> keys=replaceStrMap.keySet(); 
     for(String invalidChar:keys){ 
      int index = -1; 
      while((index=replaceStrBuilder.indexOf(invalidChar,index)) !=-1){ 
       replaceStrBuilder.replace(index,index+invalidChar.length(),replaceStrMap.get(invalidChar)); 
      } 
     } 
     return replaceStrBuilder.toString(); 
    } 
+0

Вы должны действительно добавить объяснение, почему этот код должен работать - вы также можете добавлять комментарии в самом коде - в его текущей форме он не дает никаких объяснений, которые могут помочь остальному сообществу понять, что вы сделали с решить/ответить на вопрос. – ishmaelMakitla

+0

У меня был следующий сценарий: мне нужно заменить несколько недопустимых символов некоторыми символами. Я просто переделаны немного выше кода и хотел отправить его @JUnitTest общественного недействительными testReplaceCharsNew() { \t Map карта = новый HashMap (); \t map.put (",", "/"); \t map.put (".", ""); \t map.put (";", "/"); \t Строка s = Utils.replaceCharsNew ("test; Replace, Chars, New.", Map); \t assertEquals («test/Replace/Chars/New», s); } – ramesh

1

Как насчет создания метода и пусть String.replaceAll сделать это для вас:

public static void replaceAll(StringBuilder sb, String regex, String replacement) 
{ 
    String aux = sb.toString(); 
    aux = aux.replaceAll(regex, replacement); 
    sb.setLength(0); 
    sb.append(aux);  
} 
Смежные вопросы