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

String.replaceAll(regex) делает такую ​​же замену дважды

Может ли кто-нибудь сказать мне, почему

System.out.println("test".replaceAll(".*", "a"));

Результаты в

aa

Обратите внимание, что следующее имеет тот же результат:

System.out.println("test".replaceAll(".*$", "a"));

Я тестировал это на java 6 и 7, и оба, похоже, ведут себя одинаково. Я что-то упустил или это ошибка в ядре regex engine?

4b9b3361

Ответ 1

Это не аномалия: .* может соответствовать чему-либо.

Вы просите заменить все вхождения:

  • первое вхождение соответствует всей строке, поэтому механизм регулярных выражений начинается с конца ввода для следующего совпадения;
  • но .* также соответствует пустой строке! Поэтому он соответствует пустой строке в конце ввода и заменяет ее на a.

Использование .+ вместо этого не будет проявлять эту проблему, так как это регулярное выражение не может соответствовать пустой строке (для этого требуется хотя бы один символ).

Или используйте .replaceFirst(), чтобы заменить только первое вхождение:

"test".replaceFirst(".*", "a")
       ^^^^^^^^^^^^

Теперь, почему .* ведет себя так, как это делает, и не соответствует более двух раз (теоретически это может быть), это интересно рассмотреть. См. Ниже:

# Before first run
regex: |.*
input: |whatever
# After first run
regex: .*|
input: whatever|
#before second run
regex: |.*
input: whatever|
#after second run: since .* can match an empty string, it it satisfied...
regex: .*|
input: whatever|
# However, this means the regex engine matched an empty input.
# All regex engines, in this situation, will shift
# one character further in the input.
# So, before third run, the situation is:
regex: |.*
input: whatever<|ExhaustionOfInput>
# Nothing can ever match here: out

Заметим, что, как @A.H. примечания в комментариях, не все двигатели регулярных выражений ведут себя таким образом. GNU sed, например, рассмотрит, что он исчерпал вход после первого совпадения.