Подтвердить что ты не робот

Как заменить строку только один раз без регулярного выражения в Java?

Мне нужно заменить динамическую подстроку с большей строкой, но только один раз (например, первое совпадение). Класс String предоставляет только replace(), который заменяет ВСЕ экземпляры подстроки; существует метод replaceFirst(), но вместо регулярной строки он принимает только регулярное выражение. У меня две проблемы с использованием regex:

1) моя подстрока динамична, поэтому может содержать странные символы, которые означают что-то еще в регулярном выражении, и я не хочу иметь дело с символом, ускоряющимся.

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

Мне, должно быть, что-то не хватает, потому что это кажется мне очень простой... Есть ли метод replaceFirst, который принимает правильную строку где-то еще в java-franework?

4b9b3361

Ответ 1

Используйте bigString.indexof(smallString), чтобы получить индекс первого вхождения маленькой строки в большой (или -1, если нет, и в этом случае вы закончили). Затем используйте bigString.substring, чтобы получить фрагменты большой строки до и после матча, и, наконец, concat, чтобы помещать их до и после фрагментов вместе, с вашей предполагаемой заменой в середине.

Ответ 2

Вы должны использовать уже проверенные и хорошо документированные библиотеки в пользу написания собственного кода!

StringUtils.replaceOnce("aba", "a", "")    = "ba"

Ответ 3

Как предложил Лоуренс, вы можете использовать Pattern.quote следующим образом:

newString = string.replaceFirst(Pattern.quote(substring), replacement);

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

newString = Pattern.compile(substring, Pattern.LITERAL).
        matcher(string).replaceFirst(replacement);

Это может быть немного более эффективным, но также немного менее понятным.

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

Ответ 4

обязательно замените \s и $s на замену. Вероятно, это именно то, чего вы хотите, потому что у вас не может быть никаких подгрупп с удаленным вашим ()).

newStr = str.replaceFirst(Pattern.quote(target), Matcher.quoteReplacement(replacement));

Ответ 5

Pattern.quote, похоже, не работает во всех случаях. Попробуйте `Pattern.quote( ":-)" );

Ответ 6

Класс StringBuilder в java можно очень легко использовать для замены одной строки другой, не связанной с regex.

private static String replace(String in, String ths, String that) {

    StringBuilder sb = new StringBuilder(in);

    int idx = sb.indexOf(ths); 

    while (idx > -1)
        sb.replace(idx, idx + ths.length(), that);
        idx = sb.indexOf(ths);
    }

    return sb.toString(); 

}

Ответ 7

Решение с Pattern

Вы также можете изменить метод String.replace, который использует интерпретацию символов буквально для замены только первое вхождение последовательности символов target:

/**
 * Replaces the first subsequence of the <tt>source</tt> character sequence
 * that matches the literal target sequence with the specified literal
 * replacement sequence.
 * 
 * @param source source sequence on which the replacement is made
 * @param target the sequence of char values to be replaced
 * @param replacement the replacement sequence of char values
 * @return the resulting string
 */
private static String replaceFirst1(CharSequence source, CharSequence target, CharSequence replacement) {
    return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
        source).replaceFirst(Matcher.quoteReplacement(replacement.toString()));
}


Из документации Pattern.LITERAL:

Когда этот флаг указан, входная строка, указывающая шаблон, рассматривается как последовательность буквенных символов. Метасимволы или escape-последовательности во входной последовательности не будут иметь особого значения.


Решение без Pattern

Другим и более эффективным способом, конечно же, является использование Alex Martelli подсказки для создания следующих функций:

/**
 * Replaces the first subsequence of the <tt>source</tt> string that matches
 * the literal target string with the specified literal replacement string.
 * 
 * @param source source string on which the replacement is made
 * @param target the string to be replaced
 * @param replacement the replacement string
 * @return the resulting string
 */
private static String replaceFirst2(String source, String target, String replacement) {
    int index = source.indexOf(target);
    if (index == -1) {
        return source;
    }

    return source.substring(0, index)
        .concat(replacement)
        .concat(source.substring(index+target.length()));
}


Измерение времени

На основе 10 запусков метод replaceFirst2 выполняет в 5 раз быстрее, чем метод replaceFirst1. Я поставил оба эти метода в цикл с 100 000 повторами и получил следующие результаты в миллисекундах:

    Method                 Results (in ms)             Average
replaceFirst1: 220 187 249 186 199 211 172 199 281 199 | 210
replaceFirst2:  40  39  58  45  48  40  42  42  43  59 |  45