Ответ 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
, например, рассмотрит, что он исчерпал вход после первого совпадения.