Подтвердить что ты не робот

Почему в этом 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"
4b9b3361

Ответ 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 сообщает движку снова попробовать весь шаблон. Теперь нет ничего, что можно было бы сопоставить - ни регулярных символов, ни конечной пустой строки - поэтому процесс останавливается.