Почему в этом Perl-регулярном выражении не потребляется вся строка?
Почему не первый вывод на печать выражение, что я ожидаю:
first = This is a test string, sec = This is a test string
Так как и * и + являются жадными, то почему внутреннее * i.e внутри "((" в первом совпадении не потребляет всю строку?
use strict;
use warnings;
my $string = "This is a test string";
$string =~ /((.*)*)/;
print "first = $1, sec = $2\n"; #prints "first = This is a test string, sec ="
$string =~ /((.+)*)/;
print "first = $1, sec = $2\n"; #prints "first = This is a test string, sec = This is a test string"
Ответы
Ответ 1
В первом регулярном выражении .*
сопоставляется два раза. Первый раз он соответствует всей строке. Второй раз он соответствует пустой строке в конце, потому что .*
соответствует пустой строке, когда нет ничего, что можно было бы сопоставить.
Это не происходит с другим регулярным выражением, потому что .+
не может соответствовать пустой строке.
Изменить: Что касается того, где идет: $2 будет содержать то, что соответствует последнему моменту .*
/.+
. $1 будет содержать то, что соответствует (.*)*
/(.+)*
, т.е. Целая строка.
Ответ 2
Запуск с помощью use re 'debug'
" приводит к:
Compiling REx "((.*)*)"
Final program:
1: OPEN1 (3)
3: CURLYX[0] {0,32767} (12)
5: OPEN2 (7)
7: STAR (9) # <====
8: REG_ANY (0)
9: CLOSE2 (11)
11: WHILEM[1/1] (0)
12: NOTHING (13)
13: CLOSE1 (15)
15: END (0)
minlen 0
Matching REx "((.*)*)" against "This is a test string"
0 <> <This is a > | 1:OPEN1(3)
0 <> <This is a > | 3:CURLYX[0] {0,32767}(12)
0 <> <This is a > | 11: WHILEM[1/1](0)
whilem: matched 0 out of 0..32767
0 <> <This is a > | 5: OPEN2(7)
0 <> <This is a > | 7: STAR(9) # <====
REG_ANY can match 21 times out of 2147483647...
21 < test string> <> | 9: CLOSE2(11)
21 < test string> <> | 11: WHILEM[1/1](0)
whilem: matched 1 out of 0..32767
21 < test string> <> | 5: OPEN2(7)
21 < test string> <> | 7: STAR(9) # <====
# This is where the outputs really start to diverge
# --------------------------------------------------------------------------------------------
REG_ANY can match 0 times out of 2147483647...
21 < test string> <> | 9: CLOSE2(11) # <==== Succeeded
21 < test string> <> | 11: WHILEM[1/1](0)
whilem: matched 2 out of 0..32767
whilem: empty match detected, trying continuation...
# --------------------------------------------------------------------------------------------
21 < test string> <> | 12: NOTHING(13)
21 < test string> <> | 13: CLOSE1(15)
21 < test string> <> | 15: END(0)
Match successful!
Compiling REx "((.+)*)"
Final program:
1: OPEN1 (3)
3: CURLYX[0] {0,32767} (12)
5: OPEN2 (7)
7: PLUS (9) # <====
8: REG_ANY (0)
9: CLOSE2 (11)
11: WHILEM[1/1] (0)
12: NOTHING (13)
13: CLOSE1 (15)
15: END (0)
minlen 0
Matching REx "((.+)*)" against "This is a test string"
0 <> <This is a > | 1:OPEN1(3)
0 <> <This is a > | 3:CURLYX[0] {0,32767}(12)
0 <> <This is a > | 11: WHILEM[1/1](0)
whilem: matched 0 out of 0..32767
0 <> <This is a > | 5: OPEN2(7)
0 <> <This is a > | 7: PLUS(9) # <====
REG_ANY can match 21 times out of 2147483647...
21 < test string> <> | 9: CLOSE2(11)
21 < test string> <> | 11: WHILEM[1/1](0)
whilem: matched 1 out of 0..32767
21 < test string> <> | 5: OPEN2(7)
21 < test string> <> | 7: PLUS(9) # <====
# This is where the outputs really start to diverge
# ------------------------------------------------------------------------------------
REG_ANY can match 0 times out of 2147483647...
failed... # <==== Failed
whilem: failed, trying continuation...
# ------------------------------------------------------------------------------------
21 < test string> <> | 12: NOTHING(13)
21 < test string> <> | 13: CLOSE1(15)
21 < test string> <> | 15: END(0)
Match successful!
Ответ 3
Проблема с первым регулярным выражением представляет собой комбинацию того факта, что ()*
сохраняет только последнее совпадение, а .*
соответствует пустой строке (т.е. ничего). Итак, учитывая
"aaab" =~ /(.)*/;
$1
будет "b"
. Если вы комбинируете это поведение с тем фактом, что .*
соответствует пустой строке, вы можете увидеть, что есть два совпадения внутреннего захвата: "Это тестовая строка" и "". Так как пустая строка была последней, она сохраняется в $2
. $1
- весь захват, поэтому он эквивалентен "This is a test string" . ""
. Второй случай работает так, как вы ожидаете, потому что .+
не будет соответствовать пустой строке.
Ответ 4
У меня нет ответа, но у меня есть другой способ создания проблемы, используя более простые и, возможно, более реалистичные регулярные выражения.
Первые два примера ведут себя точно так, как я ожидаю: .*
потребляет всю строку, а регулярное выражение возвращает список только с одним элементом. Но третье регулярное выражение возвращает список с двумя элементами.
use strict;
use warnings;
use Data::Dumper;
$_ = "foo";
print Dumper( [ /^(.*)/g ] ); # ('foo') As expected.
print Dumper( [ /.(.*)/g ] ); # ('oo') As expected.
print Dumper( [ /(.*)/g ] ); # ('foo', '') Why?
Многие из ответов до сих пор подчеркивали, что .*
будет соответствовать чему-либо. Хотя это правда, этот ответ не идет в центр дела, а именно: почему движок регулярного выражения все еще охотится после .*
потребляет всю строку? В других случаях (например, первые два примера) .*
не создает лишнюю пустую строку для хорошей оценки.
Обновление после полезных комментариев от Chas. Owens. Первая оценка любого из трех примеров приводит к тому, что .*
соответствует всей строке. Если бы мы могли вмешаться и называть pos()
в этот момент, двигатель действительно был бы в конце строки (по крайней мере, поскольку мы воспринимаем строку, см. Комментарии от Chas. Для более глубокого понимания этого). Тем не менее, параметр /g
указывает Perl снова попытаться совместить все регулярное выражение. Эта вторая попытка потерпит неудачу для примеров №1 и №2, и эта ошибка заставит двигатель остановить охоту. Однако, с регулярным выражением № 3, движок получит другое совпадение: пустая строка. Затем параметр /g
сообщает движку снова попробовать весь шаблон. Теперь нет ничего, что можно было бы сопоставить - ни регулярных символов, ни конечной пустой строки - поэтому процесс останавливается.