Поиск и замена в строке с несколькими фразами одновременно

Я хотел бы заменить этот код на более эффективный:

String s = "The brown fox is jumping over a fence";
s = s.replace(s, "brown", "black");
s = s.replace(s, "fox", "dog");
s = s.replace(s, "fence", "pond");
System.out.println(s);

Приведенный выше код выполняется через длинный текст 3 раза и каждый раз создает новые строки. Есть ли какая-нибудь библиотека, которая может выполнить ту же работу всего за одну итерацию по длинному тексту.

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

Но я не хочу реализовывать сам, ищу для этого уже существующий библиотечный метод. (Apache, Google, ....)

Спасибо.

Map<String, String> m = Map.of("brown", "black", "fox", "dog", "fence", "pond");String s2 = new Scanner(s).tokens().map(t ->m.getOrDefault(t, t)).collect(Collectors.joining(" ")); было бы довольно эффективно
g00se 30.07.2024 12:36

@ g00se Ваше решение также может иметь проблемы, если какой-либо токен окажется подстрокой более крупного слова, например. fenced будет заменен вашим однострочником.

Tim Biegeleisen 30.07.2024 15:53

@TimBiegeleisen Нет, потому что токеном будет fenced, который не является ключом в Map, ключи которого не работают через подстроку

g00se 30.07.2024 16:37
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
3
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

String s = "The brown fox is jumping over a fence";
String pattern = "\\b(?:brown|fox|fence)\\b";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(s);
StringBuffer buffer = new StringBuffer();

while (m.find()) {
    switch (m.group()) {
        case "brown":
            m.appendReplacement(buffer, "black");
            break;
        case "fox":
            m.appendReplacement(buffer, "dog");
            break;
        case "fence":
            m.appendReplacement(buffer, "pond");
            break;
    }
}

m.appendTail(buffer);

System.out.println(buffer.toString());    // The black dog is jumping over a pond

Приведенный выше код очень многословен, но если вы выполняли эту замену для очень большого текста (например, от 100+ МБ до ГБ), тогда можно было бы повысить производительность, просто выполняя один проход по тексту, а не отдельные проходы. для каждого поискового запроса.

Вы можете попробовать StringUtils от Apache.

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>

StringUtils.replaceEachRepeatedly();
StringUtils.replaceEach()

/**
 * <p>
 * Replaces all occurrences of Strings within another String.
 * </p>
 *
 * <p>
 * A {@code null} reference passed to this method is a no-op, or if
 * any "search string" or "string to replace" is null, that replace will be
 * ignored.
 * </p>
 *
 * <pre>
 *  StringUtils.replaceEachRepeatedly(null, *, *) = null
 *  StringUtils.replaceEachRepeatedly("", *, *) = ""
 *  StringUtils.replaceEachRepeatedly("aba", null, null) = "aba"
 *  StringUtils.replaceEachRepeatedly("aba", new String[0], null) = "aba"
 *  StringUtils.replaceEachRepeatedly("aba", null, new String[0]) = "aba"
 *  StringUtils.replaceEachRepeatedly("aba", new String[]{"a"}, null) = "aba"
 *  StringUtils.replaceEachRepeatedly("aba", new String[]{"a"}, new String[]{""}) = "b"
 *  StringUtils.replaceEachRepeatedly("aba", new String[]{null}, new String[]{"a"}) = "aba"
 *  StringUtils.replaceEachRepeatedly("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}) = "wcte"
 *  (example of how it repeats)
 *  StringUtils.replaceEachRepeatedly("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}) = "tcte"
 *  StringUtils.replaceEachRepeatedly("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}) = IllegalStateException
 * </pre>
 *
 * @param text
 *            text to search and replace in, no-op if null
 * @param searchList
 *            the Strings to search for, no-op if null
 * @param replacementList
 *            the Strings to replace them with, no-op if null
 * @return the text with any replacements processed, {@code null} if
 *         null String input
 * @throws IllegalStateException
 *             if the search is repeating and there is an endless loop due
 *             to outputs of one being inputs to another
 * @throws IllegalArgumentException
 *             if the lengths of the arrays are not the same (null is ok,
 *             and/or size 0)
 * @since 2.4
 */
public static String replaceEachRepeatedly(final String text, final String[] searchList, final String[] replacementList) {
    // timeToLive should be 0 if not used or nothing to replace, else it's
    // the length of the replace array
    final int timeToLive = searchList == null ? 0 : searchList.length;
    return replaceEach(text, searchList, replacementList, true, timeToLive);
}

/**
 * <p>
 * Replaces all occurrences of Strings within another String.
 * </p>
 *
 * <p>
 * A {@code null} reference passed to this method is a no-op, or if
 * any "search string" or "string to replace" is null, that replace will be
 * ignored. This will not repeat. For repeating replaces, call the
 * overloaded method.
 * </p>
 *
 * <pre>
 *  StringUtils.replaceEach(null, *, *)        = null
 *  StringUtils.replaceEach("", *, *)          = ""
 *  StringUtils.replaceEach("aba", null, null) = "aba"
 *  StringUtils.replaceEach("aba", new String[0], null) = "aba"
 *  StringUtils.replaceEach("aba", null, new String[0]) = "aba"
 *  StringUtils.replaceEach("aba", new String[]{"a"}, null)  = "aba"
 *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""})  = "b"
 *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"})  = "aba"
 *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"})  = "wcte"
 *  (example of how it does not repeat)
 *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"})  = "dcte"
 * </pre>
 *
 * @param text
 *            text to search and replace in, no-op if null
 * @param searchList
 *            the Strings to search for, no-op if null
 * @param replacementList
 *            the Strings to replace them with, no-op if null
 * @return the text with any replacements processed, {@code null} if
 *         null String input
 * @throws IllegalArgumentException
 *             if the lengths of the arrays are not the same (null is ok,
 *             and/or size 0)
 * @since 2.4
 */
public static String replaceEach(final String text, final String[] searchList, final String[] replacementList) {
    return replaceEach(text, searchList, replacementList, false, 0);
}

Это также делает свою работу. Почему вы минусуете? Когда вы просматриваете код StringUtils, он использует тот же поиск по регулярным выражениям и соединение StringBuilder, что и другое решение, упомянутое здесь.

Hasan Tuncay 30.07.2024 13:50

Другие вопросы по теме