Заменить несколько подстрок одновременно - PullRequest
8 голосов
/ 05 октября 2011

Скажем, у меня есть файл, который содержит текст.В нем есть подстроки типа "substr1", "substr2", "substr3" и т. Д.Мне нужно заменить все эти подстроки другим текстом, например, «repl1», «repl2», «repl3».В Python я бы создал словарь, подобный этому:

{
 "substr1": "repl1",
 "substr2": "repl2",
 "substr3": "repl3"
}

, и создал бы шаблон, соединяющий клавиши с помощью '|', а затем заменил на функцию re.sub.Есть ли подобный простой способ сделать это в Java?

Ответы [ 5 ]

14 голосов
/ 05 октября 2011

Вот как ваше Python-предложение переводится на Java:

Map<String, String> replacements = new HashMap<String, String>() {{
    put("substr1", "repl1");
    put("substr2", "repl2");
    put("substr3", "repl3");
}};

String input = "lorem substr1 ipsum substr2 dolor substr3 amet";

// create the pattern joining the keys with '|'
String regexp = "substr1|substr2|substr3";

StringBuffer sb = new StringBuffer();
Pattern p = Pattern.compile(regexp);
Matcher m = p.matcher(input);

while (m.find())
    m.appendReplacement(sb, replacements.get(m.group()));
m.appendTail(sb);


System.out.println(sb.toString());   // lorem repl1 ipsum repl2 dolor repl3 amet

Этот подход заменяет одновременную (т.е. "сразу"). То есть, если у вас было

"a" -> "b"
"b" -> "c"

тогда этот подход даст "a b" -> "b c", в отличие от ответов, предлагающих вам соединить несколько вызовов на replace или replaceAll, что даст "c c".


(Если вы обобщаете этот подход для создания регулярного выражения программно, убедитесь, что вы Pattern.quote каждое отдельное слово для поиска и Matcher.quoteReplacement каждое слово для замены.)

6 голосов
/ 05 октября 2011

StringUtils.replaceEach в проекте Apache Commons Lang , но он работает со строками.

2 голосов
/ 05 октября 2011
yourString.replace("substr1", "repl1")
          .replace("substr2", "repl2")
          .replace("substr3", "repl3");
1 голос
/ 27 ноября 2013

Сначала демонстрация проблемы:

String s = "I have three cats and two dogs.";
s = s.replace("cats", "dogs")
    .replace("dogs", "budgies");
System.out.println(s);

Это предназначено для замены кошек => собак и собак => волнистых попугайчиков, но последовательная замена действует на результат предыдущей замены, поэтому неудачный вывод:

У меня три волнистых попугая и два волнистых попугая.

Вот моя реализация метода одновременной замены. Это легко написать, используя String.regionMatches:

public static String simultaneousReplace(String subject, String... pairs) {
    if (pairs.length % 2 != 0) throw new IllegalArgumentException(
        "Strings to find and replace are not paired.");
    StringBuilder sb = new StringBuilder();
    int numPairs = pairs.length / 2;
    outer:
    for (int i = 0; i < subject.length(); i++) {
        for (int j = 0; j < numPairs; j++) {
            String find = pairs[j * 2];
            if (subject.regionMatches(i, find, 0, find.length())) {
                sb.append(pairs[j * 2 + 1]);
                i += find.length() - 1;
                continue outer;
            }
        }
        sb.append(subject.charAt(i));
    }
    return sb.toString();
}

Тестирование:

String s = "I have three cats and two dogs.";
s = simultaneousReplace(s,
    "cats", "dogs",
    "dogs", "budgies");
System.out.println(s);

Выход:

У меня три собаки и два волнистых попугая.

Кроме того, иногда полезно выполнять одновременную замену, чтобы убедиться в том, что найдено самое длинное совпадение. (Например, функция PHP strtr делает это.) Вот моя реализация для этого:

public static String simultaneousReplaceLongest(String subject, String... pairs) {
    if (pairs.length % 2 != 0) throw new IllegalArgumentException(
        "Strings to find and replace are not paired.");
    StringBuilder sb = new StringBuilder();
    int numPairs = pairs.length / 2;
    for (int i = 0; i < subject.length(); i++) {
        int longestMatchIndex = -1;
        int longestMatchLength = -1;
        for (int j = 0; j < numPairs; j++) {
            String find = pairs[j * 2];
            if (subject.regionMatches(i, find, 0, find.length())) {
                if (find.length() > longestMatchLength) {
                    longestMatchIndex = j;
                    longestMatchLength = find.length();
                }
            }
        }
        if (longestMatchIndex >= 0) {
            sb.append(pairs[longestMatchIndex * 2 + 1]);
            i += longestMatchLength - 1;
        } else {
            sb.append(subject.charAt(i));
        }
    }
    return sb.toString();
}

Зачем вам это нужно? Пример следует:

String truth = "Java is to JavaScript";
truth += " as " + simultaneousReplaceLongest(truth,
    "Java", "Ham",
    "JavaScript", "Hamster");
System.out.println(truth);

Выход:

Ява для JavaScript, как Хэм для Хомяка

Если бы мы использовали simultaneousReplace вместо simultaneousReplaceLongest, на выходе был бы "HamScript" вместо "Hamster":)

Обратите внимание, что вышеупомянутые методы чувствительны к регистру. Если вам нужны версии без учета регистра, легко изменить вышеприведенное, потому что String.regionMatches может принимать параметр ignoreCase.

0 голосов
/ 05 октября 2011
    return yourString.replaceAll("substr1","relp1").
                     replaceAll("substr2","relp2").
                     replaceAll("substr3","relp3")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...