2015-06-21 4 views
1

Я хочу заменить определенные подстроки на замену, определенную в пределах Hashmap. Примером может служить следующее:Замените несколько подстрок в строке

import java.util.HashMap; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

public class RegexTest { 
    private static HashMap<String, String> conversionTable; 

    public static void main(String[] args) { 
     initConversionTable(); 
     System.out.println(convertNumbers("la1")); 
     System.out.println(convertNumbers("la1, lb3")); 
    } 

    private static String convertNumbers(String text) { 
     String regex = "([aub][1-3])"; 
     Matcher m = Pattern.compile(regex).matcher(text); 
     while(m.find()) { 
      text = m.replaceAll(conversionTable.get(m.group())); 
     } 

     return text; 
    } 

    private static void initConversionTable() { 
     conversionTable = new HashMap<>(); 
     conversionTable.put("a1", "A1"); 
     conversionTable.put("a2", "A2"); 
     conversionTable.put("a3", "A3"); 
     conversionTable.put("u1", "U1"); 
     conversionTable.put("u2", "U2"); 
     conversionTable.put("u3", "U3"); 
     conversionTable.put("b1", "B1"); 
     conversionTable.put("b2", "B2"); 
     conversionTable.put("b3", "B3"); 
    } 
} 

Для входных данных

la1 
la1, lb3 

Ожидаемый результат должен быть

lA1 
lA1, lB3 

но

lA1 
lA1, lA1 

До сих пор я не удалось нахождение решения с помощью Ма tcher. Есть ли такое решение? Конечно, я мог бы пройти через каждую запись HashMap и сделать замену, но я хотел бы решить ее с помощью Matcher, потому что я боюсь, что для очень длинного Hashmap и многих строк это может повлиять на производительность.

Большое спасибо!

+0

Как объясняется в принятой проблеме, проблема заключается в том, что 'replaceAll' заменяет все вхождения регулярного выражения в первый раз, когда будет' find() ', что предотвратит другие совпадения и правильные замены. Но для решения этой проблемы не используйте 'replaceFirst'. Вместо этого используйте надлежащий инструмент для обработки именно такого рода сценариев, когда мы хотим, чтобы одно совпадение с другой динамически определенной заменой (например, значение на основе ключа на карте). Поэтому используйте 'appendReplacement' и' appendTail' из класса Matcher. – Pshemo

+0

Я закрыл ваш вопрос как дубликат вопроса, где вы можете увидеть одно из решений, в котором используются эти методы. Вы также можете взглянуть на другие примеры, например http://stackoverflow.com/a/25081783/1393766, http://stackoverflow.com/a/23689796/1393766 – Pshemo

ответ

1

Проблема заключается в том, что вы используете метод replaceAll, чтобы это произошло.

Шаг 1:

la1 
la1, lb3 

войдет в петлю из-за m.Find() возвращает истину. Он найдет первый шаблон «la1». Теперь вы получите преобразование из таблицы «lA1» и замените ВСЕ вхождения шаблона, который является как «la1», так и «lb3». Тогда цикл не будет повторять снова, потому что выход уже

lA1 
lA1, lA1 

Чтобы устранить эту проблему, вам нужно заменить только один вхождение со значением coresponding, в противном случае вы будете заменить все из них с первой.

+0

Благодарим вас за разъяснение, теперь проблема очевидна. меня. Я заменил строку, в которой происходит замена, с помощью 'text = text.replace (m.group(), conversionTable.get (m.group()) ;;. Но почему 'text = m.replaceFirst (conversionTable.get (m.group()));' не работает, т. Е. Пустой результат? – Rolch2015

+1

Возможно, вам нужно сбросить совпадение для цикла для правильной оценки снова: https://docs.oracle.com/javase/8/docs/api/java/util/regex/Matcher.html#replaceFirst-java.lang. String- – Kyborek

+0

Я просто видел это и пытался, но это не сработало. Дальнейшее чтение должно решить проблему, но я буду придерживаться моего первого решения, иначе это усложнится. Еще раз спасибо :) – Rolch2015