Замена нескольких символов в строке в Java

#java #android #string #replace

#java

Вопрос:

У меня есть несколько строк с уравнениями в следующем формате ((a b)/(c (d*e))) .

У меня также есть текстовый файл, содержащий имена каждой переменной, например:

 a velocity
b distance
c time
 

и т.д…

Каким был бы наилучший способ для меня написать код, чтобы он подключался velocity везде a , где происходит, и distance для b , и так далее?

Комментарии:

1. Смотрите это и это

Ответ №1:

Не используйте String#replaceAll в этом случае, если есть небольшая вероятность, что часть, которую вы замените, будет содержать подстроку, которую вы захотите заменить позже, например "distance" , contains a , и если вы захотите заменить a позже, у "velocity" вас получится "disvelocityance" .

Это может быть такая же проблема, как если бы вы хотели заменить A на B и B на A . Для такого рода манипуляций с текстом вы можете использовать appendReplacement и appendTail из Matcher класса. Вот пример

 String input = "((a b)/(c (d*e)))";

Map<String, String> replacementsMap = new HashMap<>();
replacementsMap.put("a", "velocity");
replacementsMap.put("b", "distance");
replacementsMap.put("c", "time");

StringBuffer sb = new StringBuffer();
Pattern p = Pattern.compile("\b(a|b|c)\b");
Matcher m = p.matcher(input);
while (m.find())
    m.appendReplacement(sb, replacementsMap.get(m.group()));
m.appendTail(sb);

System.out.println(sb);
 

Вывод:

 ((velocity distance)/(time (d*e)))
 

Этот код попытается найти каждое вхождение a or b или c , которое не является частью некоторого слова (у него нет ни одного символа до или после него — сделано с помощью b которого представляет границы слов). appendReplacement это метод, который будет добавлять к тексту StringBuffer из последнего совпадения (или с начала, если это первое совпадение), но заменит найденное совпадение новым словом (я получаю замену из Map). appendTail будет помещен в текст StringBuilder после последнего совпадения.


Также, чтобы сделать этот код более динамичным, регулярное выражение должно генерироваться автоматически на основе ключей, используемых в Map. Для этого можно использовать этот код

 StringBuilder regexBuilder = new StringBuilder("\b(");
for (String word:replacementsMap.keySet())
    regexBuilder.append(Pattern.quote(word)).append('|');
regexBuilder.deleteCharAt(regexBuilder.length()-1);//lets remove last "|"
regexBuilder.append(")\b");
String regex = regexBuilder.toString();
 

Комментарии:

1. Совпадение границ «\ b», которое у вас уже есть в шаблоне, будет совпадать только с точным словом, поэтому replaceAll также будет работать.

2. @bdrx Не совсем. replaceAll это не очень хороший подход для решения проблемы множественной замены. 1) он повторяет строку несколько раз, 2) что, если OP захочет изменить wor1 с word2 помощью и word2 с word1 помощью?

3. @Pshemo Для случая замены нескольких переменных, таких как wor1 = word2 и word2, которые необходимо заменить на word1 replaceAll, не является хорошим подходом. Однако для проблемной области, где исходные переменные находятся в непересекающемся наборе (как описано в задаче) с переменными замены, решение replaceAll подходит.

4. @bdrx Верно для данного конкретного случая, но мы не знаем, какие реальные значения OP захочет заменить. Вот почему я предложил более безопасный вариант.

Ответ №2:

Я бы создал HashMap, сопоставляющий имена переменных с описаниями, затем перебирал все символы в строке и заменял каждое вхождение распознанного ключа его отображением. Я бы использовал StringBuilder для создания новой строки.

Ответ №3:

Использование hashmap и итерация по строке, как предложил Boschman, является одним из хороших решений.

Другим решением было бы сделать то, что предлагали другие, и выполнить .replaceAll() ; однако вы хотели бы использовать регулярное выражение, чтобы указать, что заменяются только слова, соответствующие полному имени переменной, а не подстроке. Регулярное выражение, использующее сопоставление границ слова ‘ b’, обеспечит это решение.

 String variable = "a";
String newVariable = "velocity";
str.replaceAll("\b"   variable   "\b", newVariable);
 

См. http://docs.oracle.com/javase/tutorial/essential/regex/bounds.html

Комментарии:

1. Но разве он не найдет c в «velocity»? Это целая строка c, а не подстрока. Я не очень хорошо знаком с регулярными выражениями, вот почему я спрашиваю.

2. Нет, он не найдет c в скорости. Он соответствует только строке с префиксом, за которой следует граница слова. Это любой символ, соответствующий (W), который не является символом слова (не соответствует w)

3. Хорошо, я понял. Это действительно сработало бы. Однако, если входная строка может иметь формат ‘ab’ вместо ‘a * b’, обычная нотация для умножения в математике, это больше не будет работать.

4. @ABoschman верно. replaceAll будет хорошим решением только для простой проблемной области, где синтаксис будет соответствовать требованиям, таким как в Java, где все переменные должны быть разделены пробелом или оператором

Ответ №4:

Для строки str используйте replaceAll() функцию:

 str = str.toUpperCase();  //Prevent substitutions of characters in the middle of a word
str = str.replaceAll("A", "velocity");
str = str.replaceAll("B", "distance");
//etc.
 

Комментарии:

1. Это небезопасный подход. Что, если замена будет содержать символ, который можно будет использовать в качестве замены другого символа? Например, если вы измените порядок и замените b сначала на distAnce , а затем замените a на velocity , вы получите disvelocityance .

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