Это связано с тем, как класс Matcher
обрабатывает шаблоны, которые могут соответствовать пустой строке. replaceAll
метод String
определяются работать таким же образом, как replaceAll
метод Matcher
, который работает следующим образом:
Этого метод сначала сбрасывает эту согласовань. Затем он сканирует входную последовательность , ища соответствия шаблону. Символы, которые не являются частью , присоединяются к строке результата; совпадение заменяется в результате заменой строки. Строка замены может содержать ссылки на захваченные подпоследовательности как в методе appendReplacement.
Когда сличитель пытается найти шаблон, если подпоследовательности в источнике является пустой строкой, сличитель возвращает пустую строку, а затем натыкается на текущий индекс 1, так что он не возвращает бесконечный цикл пустых строк. Так вот как он действует на "Hello"
:
1) Соединитель ищет .*
. Поскольку это жадное совпадение, совпадающее с максимально возможным количеством символов, оно найдет подстроку "Hello"
и использует это, заменив ее на "US"
. Текущий индекс затем позиционируется после 'o'
.
2) Соединитель ищет .*
еще раз. Поскольку он находится в конце ввода, но шаблону разрешено сопоставлять пустую строку, он соответствует пустой строке и заменяет ее другим "US"
. Но затем он подхватывает текущий индекс, который теперь находится в позиции за концом источника.
3) Соединитель снова ищет .*
, но поскольку текущий указатель находится за концом источника, он ничего не найдет.
Чтобы узнать, как это работает, попробуйте использовать ".*?"
как шаблон.Теперь совпадение будет всегда использовать пустую строку, потому что ?
сообщает ей использовать самую короткую строку. Он также увеличивает текущий индекс на 1 каждый раз, когда находит пустую строку. Результат:
a.replaceAll("(?s).*?", ".-") //returns
".-H.-e.-l.-l.-o.-"
То есть, он заменяет все пустые строки между каждой парой символов с ".-"
, и оставляет фактические символы в одиночку.
Мораль: будьте очень осторожны с шаблонами, которые могут соответствовать пустым строкам.
MORE: После прочтения комментария, в котором указать, что шаблон может быть введен пользователем, я думаю, вы могли бы использовать это как тест, чтобы увидеть, если шаблон может соответствовать пустой строке:
if ("".matches(inputPattern)) {
// ???
}
Я не уверен, что вы с ним сделаете. Возможно, это всегда так, если это правда, ваш replaceAll
добавит дополнительно US
в конце, и вы можете безопасно удалить его. Или, может быть, вы можете просто попросить их попробовать другой шаблон.
PPS. Я не уверен, где это поведение матчи (т. Е. Увеличение текущего индекса на 1, когда совпадение является пустой строкой) документировано. Я не видел этого в javaadoc Matcher
. Я полагаю, это означает, что будущая версия JRE может вести себя по-другому, хотя это кажется маловероятным.
'. *' Ноль или более раз. Поэтому он заменяет пустую строку 'US', а затем следующую непустую строку с' US' –
, что является мотивацией для * Как заменить всю строку с помощью replaceAll функция, используя регулярное выражение? *, потому что это бесполезная операция. –
'^ [\ s \ S] * $' всегда должен соответствовать всей строке ровно один раз. –