Почему символ нулевой длины всегда остается в конце исходной строки для шаблона регулярных выражений Java a? - PullRequest
1 голос
/ 28 марта 2012
Pattern pattern = Pattern.compile("a?");
Matcher matcher = pattern.matcher("a");
while(matcher.find()){
   System.out.println(matcher.start()+"["+matcher.group()+"]"+matcher.end());
}

Выход:

0[a]1
1[]1

почему это дает мне два выхода, в то время как в качестве сопоставителя используются отдельные символы.

Я заметил, что для этого шаблона он дает нулевую длину всегда в конце строки источника. Например: когда источник "abab", он дает

0[a]1
1[]1
2[a]3
3[]3
4[]4

Ответы [ 4 ]

3 голосов
/ 28 марта 2012

Специальный символ регулярного выражения ? (знак вопроса) означает «совпадать с предыдущим, ноль или один раз».

Поскольку вы выполняете сопоставление в цикле while (while (matcher.find()) {...), он находит оба совпадения выражения - одно вхождение «a» (в позиции 0, строка «a») и ноль вхождений «a» (в позиция 1, пустая строка в самом конце).

Итак, вот что соответствует вашему фрагменту кода (индексы начала / конца обозначены X/Y):

String: " a b a b "
         ├─┼─┼─┼─┤
Index:   0 1 2 3 4
Match:   ╰┬╯ ╰┬╯ ╰- the empty string 4/4 (zero occurrences of "a").
          ||  |╰- the empty string 3/3 (zero occurrences of "a").
          ||  ╰ the string "a" 2/3 (one occurrence of "a").
          |╰ the empty string 1/1 (zero occurrences of "a").
          ╰ the string "a" 0/1 (one occurrence of "a").

Он не совпадает в позициях 0/0 или 2/2, так как выражение является жадным, что означает, что он будет пытаться рассмотреть следующий символ (в позициях 0/1, 2/3) до тех пор, пока он не ' t сделать недействительным совпадение, чего не происходит, поэтому они пропускаются. Для иллюстрации, если бы вы сопоставили строку "bbbb" с шаблоном a?, вы бы получили пять пустых строк, по одной на каждую пустую строку в начале, конце и между каждым символом.

1 голос
/ 28 марта 2012

Посмотрите на

http://docs.oracle.com/javase/tutorial/essential/regex/quant.html

Он подробно объясняет ваш случай в разделе Соответствия нулевой длины

1 голос
/ 28 марта 2012

a? обозначает 0-или-1 вхождений символа a.

Пустая строка соответствует 0 вхождению.

Соответствие также является жадным в вашем случае, поэтому сначала оно соответствует 1 вхождению, а затем 0 в конце.

В случае abab думайте об этом как a[]ba[]b[], где [] обозначает найденное пустое вхождение. Сопоставитель не находит его в начале или после первого b, поскольку он может жадно совпадать с a.

0 голосов
/ 28 марта 2012

Соответствие пустому пространству после последнего символа не является универсальным.

Редактор Vim имеет такое поведение:

Буфер до:

aaaa
~
~
:s/x\?/y/g  <- command

Буфер после:

yayaya
~
~

Нет x происходит в aaaa, но x? (по умолчанию записывается x\? в Vim) допускает пустое совпадение. Шаблон соответствует пустому пространству в начале строки и между все персонажи, но не до конца.

Исключением является пустая строка. Команда заменит пустую строку одной y.

Я реализовал поведение, подобное Vim, в моей собственной программе:

$ txr -c '@(bind result @(regsub #/x?/ "y" "aaaa"))'
result="yayayaya"

$ txr -c '@(bind result @(regsub #/x?/ "y" ""))'
result="y"

Только потому, что Vim популярен, и я могу указать на это в качестве эталонной модели, если возникнут какие-либо вопросы. Но это немного взломать. Логика имеет цикл do .. while, который позволяет обрабатывать входящую пустую строку:

do {
  /* regex match, extraction, substitution ... */
  position++;
} while (position < length(input))

Таким образом, если начальная позиция равна нулю, а вход имеет нулевую длину, мы делаем цикл один раз, применяя регулярное выражение к пустой строке. Но если мы обработаем последний символ, позиция достигает длины, и цикл завершается без обработки пустой строки.

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

Поведение используемого вами Java-класса может быть реализовано так:

while (position <= length(input)) {
  /* process regex */
  position++;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...