#java #regex #string #replaceall
#java #регулярное выражение
Вопрос:
Кто-нибудь может сказать мне, почему
System.out.println("test".replaceAll(".*", "a"));
Приводит к
aa
Обратите внимание, что следующее имеет тот же результат:
System.out.println("test".replaceAll(".*$", "a"));
Я тестировал это на Java 6 и 7, и оба, похоже, ведут себя одинаково.
Я что-то упускаю или это ошибка в движке регулярных выражений java?
Ответ №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
, например, будет считать, что он исчерпал входные данные после первого совпадения.
Комментарии:
1. Согласен. Это верно и для Perl.
perl -le '$x = "test"; $x =~ s/.*/a/g; print $x'
выдает «aa».2. @ChrisDolan:
sed
выдает толькоa
, но я сомневаюсь, что это ошибка. 🙂3. @A.H. действительно, да… Мне нужно снова прочитать «Освоение регулярных выражений»
4. Спасибо за отзыв, я долгое время использую регулярные выражения, но никогда не сталкивался с этим. Каждый день изучайте что-то новое…
5. Другой способ решить эту проблему: используйте
^.*
— это будет соответствовать толькоa
один раз по очевидным причинам.
Ответ №2:
Принятый ответ еще не показал этого, поэтому вот альтернативный способ исправить ваше регулярное выражение:
System.out.println("test".replaceAll("^.*$", "a"));
Обратите внимание, я использую оба термина: ^
и $
. $
Не является строго необходимым для данного конкретного случая, но я нахожу добавление обоих наименее загадочным.