Почему ". *" И ". +" Дают разные результаты? - PullRequest
9 голосов
/ 12 ноября 2009

Почему ". *" И ". +" Дают разные результаты?

System.out.println("foo".replaceAll(".+", "bar")); // --> "bar"
System.out.println("foo".replaceAll(".*", "bar")); //--> "barbar"

Я бы ожидал "bar" для обоих, поскольку * и + оба жадные и должны соответствовать всей строке. (Приведенный выше пример - Java, но другие инструменты, такие как http://www.gskinner.com/RegExr/, дают тот же результат)

Ответы [ 7 ]

12 голосов
/ 12 ноября 2009

Вы правы в том, что оба жадны, но ".*" соответствует двум строкам: первая "foo", а вторая "" ".+" будет соответствовать только "foo".

Оба пытаются найти самую длинную строку, которая равна "foo". После этого они пытаются найти самую длинную подходящую строку после предыдущего совпадения. На этом этапе ".*" может соответствовать пустой строке, а ".+" - нет.

9 голосов
/ 12 ноября 2009

Mehrdad уже объяснил, что он также соответствует одной пустой подстроке в конце строки. Я нашел официальное объяснение этого поведения (почему совпадение одна пустая подстрока вместо бесконечного числа) в документации .net:

http://msdn.microsoft.com/en-us/library/c878ftxe.aspx

Квантификаторы *, +, {n, m} (и их «ленивые» аналоги) никогда не повторяются после пустого совпадения, когда найдено минимальное число n. Это правило не позволяет квантификаторам вводить бесконечные циклы в пустых совпадениях, когда m бесконечно (хотя правило применяется, даже если m не бесконечно).

Например, (a?) * Соответствует строке «aaa» и захватывает подстроки в шаблоне (a) (a) (a) (). Обратите внимание, что нет пятого пустого захвата, потому что четвертый пустой захват приводит к тому, что квантификатор перестает повторяться.

2 голосов
/ 12 ноября 2009

Проверено экспериментом: совпадение replaceAll не будет совпадать дважды в одной и той же позиции строки без продвижения.

Эксперимент:

System.out.println("foo".replaceAll(".??", "[bar]"));

Выход:

[bar]f[bar]o[bar]o[bar]

Пояснение:

Шаблон .?? - это не жадное совпадение, состоящее из 0 или 1 символа, что означает, что он не будет соответствовать ничему по предпочтению и одному символу, если будет принудительно. На первой итерации он ничего не соответствует, и replaceAll заменяет "" на "[bar]" в начале строки. На второй итерации он снова ничего не будет соответствовать, но это запрещено, поэтому вместо одного символа копируется с ввода на выход ("f"), позиция продвигается, сравнение повторяется и т. Д. - f - bar - o - bar - o - bar: один «[bar]» для каждого отдельного места, где может быть сопоставлена ​​пустая строка. В конце нет возможности продвинуться, поэтому замена прекращается, но только после , совпадающего с «окончательной» пустой строкой.

Просто ради любопытства Perl делает нечто очень похожее, но применяет правило по-другому, давая вывод "[bar][bar][bar][bar][bar][bar][bar]" для того же ввода и того же шаблона - .?? по-прежнему запрещено делать нулевую ширину совпадать два раза подряд в одной и той же позиции, но разрешено возвращать и сопоставлять один символ. Это означает, что он заменяет «» на «[bar]», затем заменяет «f» на «[bar]», затем «» на «[bar]», затем «o» на «[bar]» и т. Д. До конца для строки совпадение по нулевой ширине запрещено и дальнейшее совпадение по положительной ширине невозможно.

1 голос
/ 12 ноября 2009

Я предполагаю, что жадный .* сначала сопоставляет всю строку, а затем начинает искать совпадение с текущей позиции (конец строки) и сопоставляет пустую строку перед выходом.

0 голосов
/ 12 ноября 2009

Я думаю, в первом раунде оба шаблона (.+ и .*) соответствуют всей строке ("foo"). После этого оставшийся ввод, который является пустой строкой, будет сопоставлен с шаблоном .*.

Однако я обнаружил довольно странный результат из следующих закономерностей.

^.*  => 'bar'
.*$  => 'barbar'
^.*$ => 'bar'

Можете ли вы объяснить, почему он возвращает вышеуказанный результат? Чем отличается начальная строка (^) от конечной строки ($) в регулярном выражении?

Update.1

Я пытаюсь изменить входную строку на следующую строку.

Foo

Foo

Пожалуйста, посмотрите на новый результат!

'^. *' =>

бар

Foo

'. * $' =>

foo

Barbar

Так что, я думаю, есть только одна начальная строка для каждого ввода. С другой стороны, когда функция находит строку соответствия во входной строке, она не удаляет завершающую строку для текущей текущей строки. PS. Вы можете быстро попробовать это в http://gskinner.com/RegExr/

0 голосов
/ 12 ноября 2009

Это действительно интересный вопрос.

Когда вы думаете об этом, String.replaceAll(...) может быть логически реализован для выполнения одной из трех вещей в случае ". *":

  • сделать одну замену, дав "бар"
  • сделать две замены, давая "барбар"
  • попробуйте сделать бесконечное количество замен.

Ясно, что последний вариант бесполезен, поэтому я могу понять, почему они этого не сделали. Но мы не знаем, почему они выбрали «барную» интерпретацию вместо «барной» интерпретации. Проблема в том, что нет универсального стандарта для синтаксиса Regex, но есть только семантика Regex. Я предполагаю, что автор (ы) Солнца сделал одно из следующих действий:

  • посмотрите, что делали и копировали другие уже существующие реализации
  • думал об этом и делал то, что считалось лучшим, или
  • не учел этот крайний случай, и текущее поведение непреднамеренно.

Но, в конце концов, не имеет значения, ПОЧЕМУ они выбрали "barbar". Дело в том, что они это сделали ... и нам просто нужно разобраться с этим.

0 голосов
/ 12 ноября 2009

хм, Python в обоих случаях выдает 'bar':

>>> import re
>>> re.sub('.+', 'bar', 'foo')
'bar'
>>> re.sub('.*', 'bar', 'foo')
'bar'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...