#java #android #string #replace
#java
Вопрос:
У меня есть несколько строк с уравнениями в следующем формате ((a b)/(c (d*e)))
.
У меня также есть текстовый файл, содержащий имена каждой переменной, например:
a velocity
b distance
c time
и т.д…
Каким был бы наилучший способ для меня написать код, чтобы он подключался velocity
везде a
, где происходит, и distance
для b
, и так далее?
Комментарии:
Ответ №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. конечно, это будет зависеть от того, какова исходная строка. Я предполагаю, что все замены сделаны в нижнем регистре.