Процесс сопоставления заданного текста
a*
может совпасть со строкой
aaab
с первой, второй и третьей буквы, но при нахождении совпадения учитывается лишь более ранняя находка, поэтому этот a " с начала строки. После того, как это совпадение будет найдено, механизм поиска совпадения закончит работу. (Но в случае g работа будет продолжена, что мы увидим при рассмотрении примеров работы этого
Если с текущей начальной позиции в тексте совпадение не будет обнаружено, то в дело вступает механизм смещения текущей позиции поиска: состояние \A или ^, то в случае неудачи второй итерации не будет, ведь в этом случае
Как видим, поиск происходит очень скрупулезно, ведь если
Линейный алгоритм поиска не всегда обеспечивает нахождение совпадения. Рассмотрим такой пример: пусть мы имеем
\w+bb
и строку
aaabb
При сопоставлении этой строки bb ничего не останется. Но после этой неудачи не произойдет сразу итерации поиска со следующего символа, т.к. в совпавшей части \w+ будет запомнено 5 состояний: что он захватил с начала текста a, aa, aaa, aaab и aaabb.
Для хранения этих состояний требуется память, поэтому, как мы видели, максимальное значение, которое могут иметь
Т.к. для литерала b в \w+ с подстрокой aaab, далее b совпадет с символом b, а на следующий b ничего не останется. На самом деле, литералы сравниваются не по одному символу, а по целому литералу (если не указан i ), а пример выше я привел для наглядности рассуждений.
Лишь при втором уменьшении "жадности"
\w+ совпадет с aaa,
bb совпадет с bb.
Если бы текст на этом не заканчивался, то все равно было бы зафиксировано совпадение и оператор поиска вернул бы истину. Ведь $, \z или \Z. Если бы это имело место, то поиск мог бы закончиться успехом только тогда, когда $ или \Z, в тексте может еще остаться единственный символ новой строки \n.
Мы рассмотрели самый элементарный пример возврата при поиске. В
Разумеется, a{20} не могут порождать возвратов, это просто иная запись литерала.
Что будет в случае с
Если рассмотреть пример
\w{1,3}?b
и строку
aaaabb
то совпадение с первого символа строки вообще не будет найдено ни при каких значениях 1, 2 и 3. В результате получим совпадение \w{1,3}? с подстрокой aaa, и затем b совпадет с символом b. На этом поиск совпадения успешно закончится.
Рассмотрим еще один пример. Пусть мы имеем
abcd|abc|ab
и строку
ab
Когда механизм поиска обрабатывает конструкцию выбора, он пытается найти совпадение с одной и той же текущей позиции текста, пробуя для нее последовательно эти
В нашем примере сначала будет попробован abcd, эта проба закончится неудачей. Также неудачей закончится проба следующего abc. И только последний ab совпадет. Если бы после этой конструкции выбора ab ab - последний
На этом примере надо усвоить одно важное правило использования конструкций выбора: если в такой конструкции есть
(ab|abc)\w*
и строка
abc
Первый, более короткий abc. В этом случае его надо ставить раньше ab. Вот еще более очевидный пример: в конструкции выбора
.*|abc
первый *. До попытки применить вторую альтернативу очередь никогда не дойдет, как будто ее нет вовсе!
Когда происходит возврат в
Такой алгоритм поиска называется недетерминированным конечным автоматом (НКА). Этот алгоритм управляется
Рассмотрим еще примеры:
$_='abcd'; /(\w+)(\w+)/; print "$1|$2";
Сначала первая пара скобок захватит всю строку, но во имя нахождения совпадения для всего abc|d. Если во второй паре скобок вместо плюса стояла бы звездочка или вопросительный знак, то на их долю вообще не осталось бы символов - все их поглотил бы $2 получила бы пустое значение. Вот она, "жадность" в действии. Каждый "жадный"
Возьмем
12?3
и строку
123
Т.к.
в шаблоне 1.3 в строке 1.23
где точкой обозначена текущая позиция поиска. 2? совпадет с 2, а 3 совпадет с символом 3. Совпадение будет найдено с первой попытки, и сохраненное состояние не вступит в игру, а если бы и вступило, то не привело бы к успеху поиска.
Теперь рассмотрим работу этого
13
Вначале литерал 1 совпадет с символом 1 в строке. Далее 2? с максимальным значением 2, не совпадет с символом 3. Но перед этим создается сохраненное состояние:
в шаблоне 1.3 в строке 1.3
При этой локальной неудаче произойдет возврат к последнему сохраненному состоянию, который приведет к совпадению литерала 3 с символом 3, и поиск закончится удачно.
Вы можете распечатывать отладочную информацию и наблюдать процесс возвратов и итераций. Для этого надо вставить внутрь регулярного выражения код x, о котором речь пойдет также в начале этой главы:
#!/usr/bin/perl -w
use strict;
use re 'eval';
$_='abcd';
m!(?{ print "Starting from ".pos($_)."\n" })
(\w
(?{ print pos($_)."\n$1\n" })
)*!x;
Если убрать код
m!(\w)*!x;
На печать выведется следующее:
Starting from 0 1 Use of uninitialized value in concatenation (.) or string at (re_eval 2) line 1. 2 a 3 b 4 c
Конструкция
(?{ код Perl })
вставляет код
Eval-group not allowed at runtime, use re 'eval' in regex m/…
Чтобы избежать этой ошибки, нужно директивой
use re 'eval';
разрешить вставку кода
Этот код у нас распечатывает текущую позицию поиска pos в строке $_ (позиция отсчитывается от нуля) и значение переменной $1.
Вначале мы видим строку
Starting from 0
Она говорит о старте очередной итерации поиска. Эта итерация в нашем примере единственная. Далее происходит сопоставление \w и символа a, а затем - печать текущей позиции поиска (2) и значения переменной $1. Но т.к. эта печать происходит еще до закрытия $1 еще не появилась, то возникает предупреждающее сообщение об использовании неопределенной переменной. Конечно, его можно было бы избежать, если написать
print "$1\n" if defined $1;
Но я не стал излишне загромождать код.
Затем скобки захватывают символ a, и переменная $1 приобретает значение $1='a', а * заставляет вернуться за закрывающую скобку и повторить поиск, начиная от \w. Теперь \w соответствует символу b, а распечатывается текущая позиция 2 и текущее значение $1, которое равно a. Затем повторяется тот же самый цикл, который печатает очередные текущие позиции и захваченные символы. После того, как \w совпадает с d, печатается последняя позиция этой строки 4 и текущее значение $1, которое равно c. Значение d не вышло на печать, т.к. печать происходит до закрытия d.
С помощью такого диагностического вывода можно отследить, как выполняется поиск. Иногда вывод показывает слишком большое число итераций и/или возвратов, которые надо оптимизировать.
Иногда нужно, чтобы для какого-то
\s*=\s*
Представим, что вокруг знака равенства стояли бы пробелы и впоследствии ссылка не была бы найдена. Тогда произошел бы возврат и \s* отдал бы один пробел для продолжения поиска. Но мы-то знаем, что это не повлияет на нахождение ссылки, так зачем проделывать ненужную работу? Для уничтожения сохраненных состояний \s* надо заключить в специальную скобочную конструкцию:
(?>\s*)
Эти скобки не являются захватывающими. При выходе за закрывающую скобку все сохраненные состояния, созданные внутри этих скобок, будут уничтожены. В данном примере это, вообще говоря, приведет к более быстрому приходу к отрицательному результату поиска. Слова "вообще говоря" означают, что на уничтожение сохраненных состояний тоже требуется время, поэтому выигрыш (или даже проигрыш) во времени определяется каждым конкретным случаем.
Аналогично можно было бы воспользоваться
<a(?>\s+) (?>[^>]*) и т.д.
При возврате к
Рассмотрим такие примеры:
(?>\w*)c
и строку
abc
Как вы уже догадались, совпадения не будет найдено. * захватит все три символа, а после выхода за скобки сохраненные состояния будут уничтожены, поэтому этот
А что будет в случае
(?>\w*?)c
и строку
abc
Совпадение будет найдено, но только при третьей итерации. \w* захватит 0 символов, а литерал c совпадет с символом c. Если в "жадном" режиме
Если бы мы вынесли
(?>\w)*c (?>\w)*?c
то совпадения были бы найдены в обоих случаях, причем, с начала строки. Состояния
Вот более практический и сложный пример: пусть нам надо округлять числа типа
23.34000012 23.345000023 34.4000025 456.00
так, чтобы после десятичной точки оставалось минимум два знака, а третий знак чтобы присутствовал, только если он не равен нулю. В приведенном примере должно получиться так:
23.34 23.345 34.40 456.00
Округление мы будем делать оператором s/…/…/. Поставим еще условие, что он не должен ничего менять, если число уже имеет правильный вид. Тогда получалась бы замена цифр самих на себя.
Вначале нас интересует десятичная точка: \., затем - две обязательных цифры: \d\d. За ними может идти цифра от 1 до 9, а может и не идти: [1-9]?. Все это мы возьмем в захватывающие скобки, чтобы этим заменить все от точки до конца числа. А после еще могут идти цифры: \d*, они будут удалены. Оператор получается такой:
s/(\.\d\d[1-9]?)\d*/$1/;
Но этот оператор делает замену, к примеру, в числе 23.345, меняя найденное на себя, т.е. выполняя ненужную работу. Вот пример кода:
$_='23.345'; print s/(\.\d\d[1-9]?)\d*/$1/."\n"; print $_;
Он выведет
1 23.345
Напомню, что оператор s возвращает число успешно сделанных замен. Вот, как это происходит:
(\.\d\d[1-9]?)
совпадает с
.345
Далее цифр нет, поэтому \d* совпадает с пустой подстрокой, и т.к. совпадение найдено, то замена срабатывает. А она должна срабатывать только, если после .345 есть цифра. Давайте для этого попробуем заменить \d* на \d+ и посмотрим, что получится. А получается результат
1 23.34
Третья цифра обрезается. Это происходит потому, что теперь \d+ совпасть не с чем. Но ведь у [1-9]?. Происходит возврат внутрь скобок к этому .34, а \d+ совпадает с третьей цифрой 5, поэтому пятерка из результата удаляется. Нам нужно, чтобы при совпадении [1-9]? с третьей цифрой это совпадение не отменялось. Но для этого надо удалить данное сохраненное состояние, т.е. заключить [1-9]? в атомарные скобки:
s/(\.\d\d(?>[1-9]?))\d+/$1/;
Теперь пример работает правильно.
Процесс сопоставления заданного текста
a*
может совпасть со строкой
aaab
с первой, второй и третьей буквы, но при нахождении совпадения учитывается лишь более ранняя находка, поэтому этот a " с начала строки. После того, как это совпадение будет найдено, механизм поиска совпадения закончит работу. (Но в случае g работа будет продолжена, что мы увидим при рассмотрении примеров работы этого
Если с текущей начальной позиции в тексте совпадение не будет обнаружено, то в дело вступает механизм смещения текущей позиции поиска: состояние \A или ^, то в случае неудачи второй итерации не будет, ведь в этом случае
Как видим, поиск происходит очень скрупулезно, ведь если
Линейный алгоритм поиска не всегда обеспечивает нахождение совпадения. Рассмотрим такой пример: пусть мы имеем
\w+bb
и строку
aaabb
При сопоставлении этой строки bb ничего не останется. Но после этой неудачи не произойдет сразу итерации поиска со следующего символа, т.к. в совпавшей части \w+ будет запомнено 5 состояний: что он захватил с начала текста a, aa, aaa, aaab и aaabb.
Для хранения этих состояний требуется память, поэтому, как мы видели, максимальное значение, которое могут иметь
Т.к. для литерала b в \w+ с подстрокой aaab, далее b совпадет с символом b, а на следующий b ничего не останется. На самом деле, литералы сравниваются не по одному символу, а по целому литералу (если не указан i ), а пример выше я привел для наглядности рассуждений.
Лишь при втором уменьшении "жадности"
\w+ совпадет с aaa,
bb совпадет с bb.
Если бы текст на этом не заканчивался, то все равно было бы зафиксировано совпадение и оператор поиска вернул бы истину. Ведь $, \z или \Z. Если бы это имело место, то поиск мог бы закончиться успехом только тогда, когда $ или \Z, в тексте может еще остаться единственный символ новой строки \n.
Мы рассмотрели самый элементарный пример возврата при поиске. В
Разумеется, a{20} не могут порождать возвратов, это просто иная запись литерала.
Что будет в случае с
Если рассмотреть пример
\w{1,3}?b
и строку
aaaabb
то совпадение с первого символа строки вообще не будет найдено ни при каких значениях 1, 2 и 3. В результате получим совпадение \w{1,3}? с подстрокой aaa, и затем b совпадет с символом b. На этом поиск совпадения успешно закончится.
Рассмотрим еще один пример. Пусть мы имеем
abcd|abc|ab
и строку
ab
Когда механизм поиска обрабатывает конструкцию выбора, он пытается найти совпадение с одной и той же текущей позиции текста, пробуя для нее последовательно эти
В нашем примере сначала будет попробован abcd, эта проба закончится неудачей. Также неудачей закончится проба следующего abc. И только последний ab совпадет. Если бы после этой конструкции выбора ab ab - последний
На этом примере надо усвоить одно важное правило использования конструкций выбора: если в такой конструкции есть
(ab|abc)\w*
и строка
abc
Первый, более короткий abc. В этом случае его надо ставить раньше ab. Вот еще более очевидный пример: в конструкции выбора
.*|abc
первый *. До попытки применить вторую альтернативу очередь никогда не дойдет, как будто ее нет вовсе!
Когда происходит возврат в
Такой алгоритм поиска называется недетерминированным конечным автоматом (НКА). Этот алгоритм управляется
Рассмотрим еще примеры:
$_='abcd'; /(\w+)(\w+)/; print "$1|$2";
Сначала первая пара скобок захватит всю строку, но во имя нахождения совпадения для всего abc|d. Если во второй паре скобок вместо плюса стояла бы звездочка или вопросительный знак, то на их долю вообще не осталось бы символов - все их поглотил бы $2 получила бы пустое значение. Вот она, "жадность" в действии. Каждый "жадный"
Возьмем
12?3
и строку
123
Т.к.
в шаблоне 1.3 в строке 1.23
где точкой обозначена текущая позиция поиска. 2? совпадет с 2, а 3 совпадет с символом 3. Совпадение будет найдено с первой попытки, и сохраненное состояние не вступит в игру, а если бы и вступило, то не привело бы к успеху поиска.
Теперь рассмотрим работу этого
13
Вначале литерал 1 совпадет с символом 1 в строке. Далее 2? с максимальным значением 2, не совпадет с символом 3. Но перед этим создается сохраненное состояние:
в шаблоне 1.3 в строке 1.3
При этой локальной неудаче произойдет возврат к последнему сохраненному состоянию, который приведет к совпадению литерала 3 с символом 3, и поиск закончится удачно.
Вы можете распечатывать отладочную информацию и наблюдать процесс возвратов и итераций. Для этого надо вставить внутрь регулярного выражения код x, о котором речь пойдет также в начале этой главы:
#!/usr/bin/perl -w
use strict;
use re 'eval';
$_='abcd';
m!(?{ print "Starting from ".pos($_)."\n" })
(\w
(?{ print pos($_)."\n$1\n" })
)*!x;
Если убрать код
m!(\w)*!x;
На печать выведется следующее:
Starting from 0 1 Use of uninitialized value in concatenation (.) or string at (re_eval 2) line 1. 2 a 3 b 4 c
Конструкция
(?{ код Perl })
вставляет код
Eval-group not allowed at runtime, use re 'eval' in regex m/…
Чтобы избежать этой ошибки, нужно директивой
use re 'eval';
разрешить вставку кода
Этот код у нас распечатывает текущую позицию поиска pos в строке $_ (позиция отсчитывается от нуля) и значение переменной $1.
Вначале мы видим строку
Starting from 0
Она говорит о старте очередной итерации поиска. Эта итерация в нашем примере единственная. Далее происходит сопоставление \w и символа a, а затем - печать текущей позиции поиска (2) и значения переменной $1. Но т.к. эта печать происходит еще до закрытия $1 еще не появилась, то возникает предупреждающее сообщение об использовании неопределенной переменной. Конечно, его можно было бы избежать, если написать
print "$1\n" if defined $1;
Но я не стал излишне загромождать код.
Затем скобки захватывают символ a, и переменная $1 приобретает значение $1='a', а * заставляет вернуться за закрывающую скобку и повторить поиск, начиная от \w. Теперь \w соответствует символу b, а распечатывается текущая позиция 2 и текущее значение $1, которое равно a. Затем повторяется тот же самый цикл, который печатает очередные текущие позиции и захваченные символы. После того, как \w совпадает с d, печатается последняя позиция этой строки 4 и текущее значение $1, которое равно c. Значение d не вышло на печать, т.к. печать происходит до закрытия d.
С помощью такого диагностического вывода можно отследить, как выполняется поиск. Иногда вывод показывает слишком большое число итераций и/или возвратов, которые надо оптимизировать.
Иногда нужно, чтобы для какого-то
\s*=\s*
Представим, что вокруг знака равенства стояли бы пробелы и впоследствии ссылка не была бы найдена. Тогда произошел бы возврат и \s* отдал бы один пробел для продолжения поиска. Но мы-то знаем, что это не повлияет на нахождение ссылки, так зачем проделывать ненужную работу? Для уничтожения сохраненных состояний \s* надо заключить в специальную скобочную конструкцию:
(?>\s*)
Эти скобки не являются захватывающими. При выходе за закрывающую скобку все сохраненные состояния, созданные внутри этих скобок, будут уничтожены. В данном примере это, вообще говоря, приведет к более быстрому приходу к отрицательному результату поиска. Слова "вообще говоря" означают, что на уничтожение сохраненных состояний тоже требуется время, поэтому выигрыш (или даже проигрыш) во времени определяется каждым конкретным случаем.
Аналогично можно было бы воспользоваться
<a(?>\s+) (?>[^>]*) и т.д.
При возврате к
Рассмотрим такие примеры:
(?>\w*)c
и строку
abc
Как вы уже догадались, совпадения не будет найдено. * захватит все три символа, а после выхода за скобки сохраненные состояния будут уничтожены, поэтому этот
А что будет в случае
(?>\w*?)c
и строку
abc
Совпадение будет найдено, но только при третьей итерации. \w* захватит 0 символов, а литерал c совпадет с символом c. Если в "жадном" режиме
Если бы мы вынесли
(?>\w)*c (?>\w)*?c
то совпадения были бы найдены в обоих случаях, причем, с начала строки. Состояния
Вот более практический и сложный пример: пусть нам надо округлять числа типа
23.34000012 23.345000023 34.4000025 456.00
так, чтобы после десятичной точки оставалось минимум два знака, а третий знак чтобы присутствовал, только если он не равен нулю. В приведенном примере должно получиться так:
23.34 23.345 34.40 456.00
Округление мы будем делать оператором s/…/…/. Поставим еще условие, что он не должен ничего менять, если число уже имеет правильный вид. Тогда получалась бы замена цифр самих на себя.
Вначале нас интересует десятичная точка: \., затем - две обязательных цифры: \d\d. За ними может идти цифра от 1 до 9, а может и не идти: [1-9]?. Все это мы возьмем в захватывающие скобки, чтобы этим заменить все от точки до конца числа. А после еще могут идти цифры: \d*, они будут удалены. Оператор получается такой:
s/(\.\d\d[1-9]?)\d*/$1/;
Но этот оператор делает замену, к примеру, в числе 23.345, меняя найденное на себя, т.е. выполняя ненужную работу. Вот пример кода:
$_='23.345'; print s/(\.\d\d[1-9]?)\d*/$1/."\n"; print $_;
Он выведет
1 23.345
Напомню, что оператор s возвращает число успешно сделанных замен. Вот, как это происходит:
(\.\d\d[1-9]?)
совпадает с
.345
Далее цифр нет, поэтому \d* совпадает с пустой подстрокой, и т.к. совпадение найдено, то замена срабатывает. А она должна срабатывать только, если после .345 есть цифра. Давайте для этого попробуем заменить \d* на \d+ и посмотрим, что получится. А получается результат
1 23.34
Третья цифра обрезается. Это происходит потому, что теперь \d+ совпасть не с чем. Но ведь у [1-9]?. Происходит возврат внутрь скобок к этому .34, а \d+ совпадает с третьей цифрой 5, поэтому пятерка из результата удаляется. Нам нужно, чтобы при совпадении [1-9]? с третьей цифрой это совпадение не отменялось. Но для этого надо удалить данное сохраненное состояние, т.е. заключить [1-9]? в атомарные скобки:
s/(\.\d\d(?>[1-9]?))\d+/$1/;
Теперь пример работает правильно.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.