Динамическое
(??{ код Perl })
В процессе его работы выполняется этот код
Это очень мощная конструкция, которая позволяет конструировать (? if then [ | else ]) позволял выбирать один из двух
Динамическое
$_='abc';
print 'OK' if /ab(??{'c'})/;
Динамическое c, в результате обнаруживается совпадение с текстом abc. Теперь попробуем подставить *:
$_='abc';
print 'OK' if /ab(??{'*'})/;
Получаем сообщение
Quantifier follows nothing in regex; …
При использовании динамических
panic: top_env
Это значит, что интерпретатор не может определить ошибку, которая происходит во время выполнения кода
Авторы книг и пособий упускают такой момент: при компиляции возвращаемого значения динамического
Поэтому внутри динамического \L, \U, … и оператор \Q, использованные внутри динамического
Для начала приведу простой и немного искусственный пример: пусть нам надо проверить правильность строки
Далее стоит 13 нулей: 0000000000000 Причем, число нулей может быть произвольным от 1 и более, например, Далее стоит 2 нуля: 00
Нам надо составить
(\d+) \D+(??{"0{$1}"})$
Работает он таким образом: вначале в переменную $1 захватывается число (13, 2 и т.д.).
Затем идет пропуск всех нецифровых символов. Внутри динамического $1. Для фразы
Далее стоит 13 нулей: 0000000000000
Весь
(\d+) \D+0{13}$
В случае строки
Далее стоит 2 нуля: 00
Это
(\d+) \D+0{2}$
Даже для строки
Далее стоит 0 нулей:
будет найдено совпадение.
В документации по $_ первую наидлиннейшую последовательность 10-ных цифр, которые стоят подряд и возрастают на 1. Например, в строке
$_='0123 1234345678910 ';
искомая наидлиннейшая последовательность такая: 3456789.
Вот сама программа:
#!/usr/bin/perl -w
use strict;
my ($len,$d,$res)=(0);
$_='0123 1234345678910 ';
no warnings;
/((\d)
(?{$d=$+})
(?>
(??{ ++$d < 10 ? "$d" : "(?!)" })*
)
)
(?{ # Эти символы нельзя разделять
if (length $1 > $len)
{ $len=length $1;
$res=$1;
}
}) # Эту скобку нельзя отделять от предыдущей скобки "}"
(?!)
/x;
print $res if defined $res;
Вначале переменной $len присваивается 0. В комментариях указана особенность конструкций (?{ и }), в которых фигурную скобку нельзя отделять от соседнего символа даже и при свободном форматировании.
Теперь разберемся, как работает это $d. Далее идет динамическое *. Оно вставлено в атомарные скобки.
Код в этом динамическом *, увеличивает на единицу переменную $d, и если ее значение меньше десяти, то он возвращает значение этой переменной, а если $d после инкремента принимает значение 10, которое уже не подходит для цифры, то этот код (?!), который заставляет * сделать шаг назад и остановиться на своем предыдущем состоянии, при котором было совпадение с наибольшей цифрой. После этого совпадения для атомарных скобок управление передается за них и выполняется код $1
длины переменной $len. Если больше, значит, мы нашли более длинную последовательность цифр, поэтому мы запоминаем ее в переменной $res и обновляем содержимое переменной $len, которая хранит размер найденной длиннейшей последовательности цифр. После этого кода опять стоит (?!), который заставляет все
Директива no warnings нужна, чтобы отменить предупреждение
matches null string many times in regex; …
которое появится, когда динамическое (?!).
Здесь опять замечу, что это регулярное это выражение возвращает несовпадение и используется только ради побочного эффекта (установки переменной $res ).
Насколько эффективно работает это
$1:
/((\d)
(?{$d=$+})
(?>
(??{ ++$d < 10 ? "$d" : "(?!)" })*
)
) (?{print "$1\n"})
(?{ # Эти символы нельзя разделять
if (length $1 > $len)
{ $len=length $1;
$res=$1;
}
}) # Эту скобку нельзя отделять от предыдущей скобки "}"
(?!)
/x;
print $res if defined $res;
В результате на печати получим:
0123 123 23 3 1234 234 34 4 3456789 456789 56789 6789 789 89 9 1 0 3456789
Видим, что вначале (?!). Хотелось бы, чтобы вместо этого начальная позиция при итерациях устанавливалась сразу за найденной последовательностью цифр, дабы не делать лишней работы. Но мне кажется, это невозможно, т.к. присвоение внутри pos (pos($_)=…) не дает эффекта.
Вместо этого могу показать технический прием, как можно завершить все
(?(?{$len == 4})\G(?!))
При достижении этого условия встроенный код
\G(?!)
Он говорит, что с конца предыдущего совпадения (или с начала текста при первой итерации поиска) нет совпадения. На этом все (?!), чтобы произошла итерация всего
Что будет, если убрать
/((\d)
(?{$d=$+})
(??{ ++$d < 10 ? "$d" : "(?!)" })*
) (?{print "$1\n"})
(?{ # Эти символы нельзя разделять
if (length $1 > $len)
{ $len=length $1;
$res=$1;
}
}) # Эту скобку нельзя отделять от предыдущей скобки "}"
(?!)
/x;
print $res if defined $res;
Тогда на печать выйдет гораздо больше строк:
0123 012 01 0 123 12 1 23 2 3 1234 123 12 1 234 23 2 34 3 4 3456789 345678 34567 3456 345 34 3 456789 45678 4567 456 45 4 56789 5678 567 56 5 6789 678 67 6 789 78 7 89 8 9 1 0 3456789
Из этой распечатки видно, что после выполнения второго (?!) не происходит итерация всего * имеет сохраненные состояния. Он начинает отдавать по одной захваченной цифре, пока не отдаст их все. Только после этого происходит
Изначально динамические
Рассмотрим задачу удаления всех комментариев, которые заключены в фигурные скобки, вместе с этими скобками. При этом предположим, что эти комментарии могут иметь произвольную вложенность. Т.е. надо уничтожить все правильно сбалансированные серии фигурных скобок вместе с их содержимым. От строки
$_=" {a{b{c{{d{}m}e}f}}gf{ }f";
после этого должно остаться ' {agff'.
Будем рассуждать от легкого случая к более сложным. Если бы речь шла только о первом уровне вложенности: …{…}…, то мы могли бы создать такой объект
my $level0=qr/(?>[^{}]*)/;
который соответствует всему кроме фгурных скобок. А оператор замены был бы таким:
s/\{$level0}//g;
Напоминаю, что символ } не обязательно маскировать, т.к. он в отличие от символа { не является
Эта программа корректно бы удаляла комментарии первого уровня вложенности. От строки a{b}c осталось бы ac. Но если бы мы задали этой программе строку
a{b{c}d}e
то в результате получили бы остаток
a{bd}e
Удаляются только скобки с фрагментами, которые не содержат этих скобок. Мы могли бы повторять подстановку, пока оператор s/…/…/ возвращает ненулевой результат, но нам нужен общий метод для любого уровня вложенности. С этой целью расширим наш объект
my $level1=qr/(?>[^{}]|\{$level0})*/;
Здесь мы воспользовались тем, что у нас уже есть $level0. Чтобы фрагменты текста без скобок поглощались быстрее, можно поставить +:
my $level1=qr/(?>[^{}]+|\{$level0})*/;
А чтобы не создавалось ненужных сохраненных состояний, можно это еще взять в атомарные скобки:
my $level1=qr/(?>(?>[^{}]+)|\{$level0})*/;
Программа
$_='a{b{c}d}e';
my $level0=qr/(?>[^{}]*)/;
my $level1=qr/(?>(?>[^{}]+)|\{$level0})*/;
s/\{$level1}//g;
print $_;
уже справляется со скобками второго уровня вложенности, и на печать выводится
ae
Но если ей дать текст со скобками третьего уровня вложенности, то она оставляет скобки первого уровня вложенности:
$_='a{b{c{d}}e}f';
my $level0=qr/(?>[^{}]*)/;
my $level1=qr/(?>(?>[^{}]+)|\{$level0})*/;
s/\{$level1}//g;
print $_;
Выводится
a{be}f
Мы могли бы по аналогии создать объект $level3 и т.д., но динамические $levelN для произвольного уровня вложенности:
#!/usr/bin/perl -w
use strict;
$_=" {a{b{c{{d{}m}e}f}}gf{ }f";
my $levelN;
$levelN=qr
/(?>
(?>[^{}]+)| # все кроме фигурных скобок
\{(??{$levelN})} # или текст, соответств. всему шаблону, ограниченный скобками
)* # сколько угодно раз
/x;
s/\{$levelN}//g;
print $_;
В результате получаем
{agff
Это верный результат.
Как видим, объект $levelN строится рекурсивно, используя себя в качестве своего компонента. Поэтому переменную $levelN лучше объявить заранее, чтобы не было сообщения об использовании неинициализированной переменной.
В процессе работы $levelN столько раз, сколько раз встретит открывающую фигурную скобку.
Мы могли бы перенести внешние фигурные скобки внутрь нашего объекта $levelN, тогда в операторе подстановки не нужно было бы их выписывать:
$_=" {a{b{c{{d{}m}e}f}}gf{ }f";
my $levelN;
$levelN=qr
/\{ # открывающая фигурная скобка
(?>
(?>[^{}]+)| # все кроме фигурных скобок
(??{$levelN}) # или текст, соответствующий всему шаблону
)* # сколько угодно раз
} # и закрывающая фигурная скобка
/x;
s/$levelN//g;
print $_;
Во второй альтернативе также нужно убрать фигурные скобки, т.к. в $levelN они уже присутствуют. На печати результат тот же:
{agff
Рассмотрим теперь случай, когда "скобки" представляют собой многосимвольные конструкции, например, теги <table и </table>. Раньше мы уже рассматривали такие "скобки" и выработали технический прием с заменой
Поставим задачу захватить из HTML-текста лишь самые внешние таблицы с их содержимым, а остальное отбросить. Искомая программа может выглядеть так:
$_=<<EOD;
aa<table>
<tr>
ff<table>
<tr>
</table>
<tr>
</table>bb
ssssssss
<table>
</table>
EOD
my $levelN;
$levelN=qr
"(?>
(?:(?!</?table).)+| # все символы до фрагмента </?table
<table[^>]*>(??{$levelN})</table> # или вся следующая таблица
)* # сколько угодно раз
"isx;
my @tables=m"<table[^>]*>$levelN</table>"gi;
print join "\n--\n",@tables if @tables;
Конструкция (?!</?table).)+ с помощью точки будет брать символы до встречи с фрагментом, соответствующим </?table. На печать выходит
<table> <tr> ff<table> <tr> </table> <tr> </table> -- <table> </table>
Я не стал перегружать это
По поводу использования динамических
$_='abc';
print "$1 $2" if /(a)(??{"(b)"})(c)/;
В результате напечатается
a c
Если бы мы попытались распечатать "$1 $2 $3", то на печать вышло бы то же самое и еще предупреждение об использовании неинициализированной переменной (это $3 ).
Если бы мы вставляли значение переменной, которое содержит текст (b), то результат был бы тем же:
$_='abc';
my $a='(b)';
print "$1 $2" if /(a)(??{$a})(c)/;
Вот еще аналогичный вариант с объектом
$_='abc';
my $a=qr'(b)';
print "$1 $2" if /(a)(??{$a})(c)/;
Но как только мы попытаемся внутри всего
Вот эти фрагменты кода вызывают ошибку
$_='abc';
my $a='(b)';
/(??{$a})/;
$_='abc';
print 'Found' if /(??{"(b)"})/;
$_='abc';
my $a=qr'(b)';
print 'Found' if /(??{"$a"})/;
Странно, но в случае использования объекта $a присутствует без кавычек, ошибки не возникает:
$_='abc';
my $a=qr'(b)';
print 'Found' if /(??{$a})/;
В остальных случаях
$_='abc';
my $a='(?:b)';
/(??{$a})/;
все варианты работают нормально. Поэтому не создавайте
Динамическое
(??{ код Perl })
В процессе его работы выполняется этот код
Это очень мощная конструкция, которая позволяет конструировать (? if then [ | else ]) позволял выбирать один из двух
Динамическое
$_='abc';
print 'OK' if /ab(??{'c'})/;
Динамическое c, в результате обнаруживается совпадение с текстом abc. Теперь попробуем подставить *:
$_='abc';
print 'OK' if /ab(??{'*'})/;
Получаем сообщение
Quantifier follows nothing in regex; …
При использовании динамических
panic: top_env
Это значит, что интерпретатор не может определить ошибку, которая происходит во время выполнения кода
Авторы книг и пособий упускают такой момент: при компиляции возвращаемого значения динамического
Поэтому внутри динамического \L, \U, … и оператор \Q, использованные внутри динамического
Для начала приведу простой и немного искусственный пример: пусть нам надо проверить правильность строки
Далее стоит 13 нулей: 0000000000000 Причем, число нулей может быть произвольным от 1 и более, например, Далее стоит 2 нуля: 00
Нам надо составить
(\d+) \D+(??{"0{$1}"})$
Работает он таким образом: вначале в переменную $1 захватывается число (13, 2 и т.д.).
Затем идет пропуск всех нецифровых символов. Внутри динамического $1. Для фразы
Далее стоит 13 нулей: 0000000000000
Весь
(\d+) \D+0{13}$
В случае строки
Далее стоит 2 нуля: 00
Это
(\d+) \D+0{2}$
Даже для строки
Далее стоит 0 нулей:
будет найдено совпадение.
В документации по $_ первую наидлиннейшую последовательность 10-ных цифр, которые стоят подряд и возрастают на 1. Например, в строке
$_='0123 1234345678910 ';
искомая наидлиннейшая последовательность такая: 3456789.
Вот сама программа:
#!/usr/bin/perl -w
use strict;
my ($len,$d,$res)=(0);
$_='0123 1234345678910 ';
no warnings;
/((\d)
(?{$d=$+})
(?>
(??{ ++$d < 10 ? "$d" : "(?!)" })*
)
)
(?{ # Эти символы нельзя разделять
if (length $1 > $len)
{ $len=length $1;
$res=$1;
}
}) # Эту скобку нельзя отделять от предыдущей скобки "}"
(?!)
/x;
print $res if defined $res;
Вначале переменной $len присваивается 0. В комментариях указана особенность конструкций (?{ и }), в которых фигурную скобку нельзя отделять от соседнего символа даже и при свободном форматировании.
Теперь разберемся, как работает это $d. Далее идет динамическое *. Оно вставлено в атомарные скобки.
Код в этом динамическом *, увеличивает на единицу переменную $d, и если ее значение меньше десяти, то он возвращает значение этой переменной, а если $d после инкремента принимает значение 10, которое уже не подходит для цифры, то этот код (?!), который заставляет * сделать шаг назад и остановиться на своем предыдущем состоянии, при котором было совпадение с наибольшей цифрой. После этого совпадения для атомарных скобок управление передается за них и выполняется код $1
длины переменной $len. Если больше, значит, мы нашли более длинную последовательность цифр, поэтому мы запоминаем ее в переменной $res и обновляем содержимое переменной $len, которая хранит размер найденной длиннейшей последовательности цифр. После этого кода опять стоит (?!), который заставляет все
Директива no warnings нужна, чтобы отменить предупреждение
matches null string many times in regex; …
которое появится, когда динамическое (?!).
Здесь опять замечу, что это регулярное это выражение возвращает несовпадение и используется только ради побочного эффекта (установки переменной $res ).
Насколько эффективно работает это
$1:
/((\d)
(?{$d=$+})
(?>
(??{ ++$d < 10 ? "$d" : "(?!)" })*
)
) (?{print "$1\n"})
(?{ # Эти символы нельзя разделять
if (length $1 > $len)
{ $len=length $1;
$res=$1;
}
}) # Эту скобку нельзя отделять от предыдущей скобки "}"
(?!)
/x;
print $res if defined $res;
В результате на печати получим:
0123 123 23 3 1234 234 34 4 3456789 456789 56789 6789 789 89 9 1 0 3456789
Видим, что вначале (?!). Хотелось бы, чтобы вместо этого начальная позиция при итерациях устанавливалась сразу за найденной последовательностью цифр, дабы не делать лишней работы. Но мне кажется, это невозможно, т.к. присвоение внутри pos (pos($_)=…) не дает эффекта.
Вместо этого могу показать технический прием, как можно завершить все
(?(?{$len == 4})\G(?!))
При достижении этого условия встроенный код
\G(?!)
Он говорит, что с конца предыдущего совпадения (или с начала текста при первой итерации поиска) нет совпадения. На этом все (?!), чтобы произошла итерация всего
Что будет, если убрать
/((\d)
(?{$d=$+})
(??{ ++$d < 10 ? "$d" : "(?!)" })*
) (?{print "$1\n"})
(?{ # Эти символы нельзя разделять
if (length $1 > $len)
{ $len=length $1;
$res=$1;
}
}) # Эту скобку нельзя отделять от предыдущей скобки "}"
(?!)
/x;
print $res if defined $res;
Тогда на печать выйдет гораздо больше строк:
0123 012 01 0 123 12 1 23 2 3 1234 123 12 1 234 23 2 34 3 4 3456789 345678 34567 3456 345 34 3 456789 45678 4567 456 45 4 56789 5678 567 56 5 6789 678 67 6 789 78 7 89 8 9 1 0 3456789
Из этой распечатки видно, что после выполнения второго (?!) не происходит итерация всего * имеет сохраненные состояния. Он начинает отдавать по одной захваченной цифре, пока не отдаст их все. Только после этого происходит
Изначально динамические
Рассмотрим задачу удаления всех комментариев, которые заключены в фигурные скобки, вместе с этими скобками. При этом предположим, что эти комментарии могут иметь произвольную вложенность. Т.е. надо уничтожить все правильно сбалансированные серии фигурных скобок вместе с их содержимым. От строки
$_=" {a{b{c{{d{}m}e}f}}gf{ }f";
после этого должно остаться ' {agff'.
Будем рассуждать от легкого случая к более сложным. Если бы речь шла только о первом уровне вложенности: …{…}…, то мы могли бы создать такой объект
my $level0=qr/(?>[^{}]*)/;
который соответствует всему кроме фгурных скобок. А оператор замены был бы таким:
s/\{$level0}//g;
Напоминаю, что символ } не обязательно маскировать, т.к. он в отличие от символа { не является
Эта программа корректно бы удаляла комментарии первого уровня вложенности. От строки a{b}c осталось бы ac. Но если бы мы задали этой программе строку
a{b{c}d}e
то в результате получили бы остаток
a{bd}e
Удаляются только скобки с фрагментами, которые не содержат этих скобок. Мы могли бы повторять подстановку, пока оператор s/…/…/ возвращает ненулевой результат, но нам нужен общий метод для любого уровня вложенности. С этой целью расширим наш объект
my $level1=qr/(?>[^{}]|\{$level0})*/;
Здесь мы воспользовались тем, что у нас уже есть $level0. Чтобы фрагменты текста без скобок поглощались быстрее, можно поставить +:
my $level1=qr/(?>[^{}]+|\{$level0})*/;
А чтобы не создавалось ненужных сохраненных состояний, можно это еще взять в атомарные скобки:
my $level1=qr/(?>(?>[^{}]+)|\{$level0})*/;
Программа
$_='a{b{c}d}e';
my $level0=qr/(?>[^{}]*)/;
my $level1=qr/(?>(?>[^{}]+)|\{$level0})*/;
s/\{$level1}//g;
print $_;
уже справляется со скобками второго уровня вложенности, и на печать выводится
ae
Но если ей дать текст со скобками третьего уровня вложенности, то она оставляет скобки первого уровня вложенности:
$_='a{b{c{d}}e}f';
my $level0=qr/(?>[^{}]*)/;
my $level1=qr/(?>(?>[^{}]+)|\{$level0})*/;
s/\{$level1}//g;
print $_;
Выводится
a{be}f
Мы могли бы по аналогии создать объект $level3 и т.д., но динамические $levelN для произвольного уровня вложенности:
#!/usr/bin/perl -w
use strict;
$_=" {a{b{c{{d{}m}e}f}}gf{ }f";
my $levelN;
$levelN=qr
/(?>
(?>[^{}]+)| # все кроме фигурных скобок
\{(??{$levelN})} # или текст, соответств. всему шаблону, ограниченный скобками
)* # сколько угодно раз
/x;
s/\{$levelN}//g;
print $_;
В результате получаем
{agff
Это верный результат.
Как видим, объект $levelN строится рекурсивно, используя себя в качестве своего компонента. Поэтому переменную $levelN лучше объявить заранее, чтобы не было сообщения об использовании неинициализированной переменной.
В процессе работы $levelN столько раз, сколько раз встретит открывающую фигурную скобку.
Мы могли бы перенести внешние фигурные скобки внутрь нашего объекта $levelN, тогда в операторе подстановки не нужно было бы их выписывать:
$_=" {a{b{c{{d{}m}e}f}}gf{ }f";
my $levelN;
$levelN=qr
/\{ # открывающая фигурная скобка
(?>
(?>[^{}]+)| # все кроме фигурных скобок
(??{$levelN}) # или текст, соответствующий всему шаблону
)* # сколько угодно раз
} # и закрывающая фигурная скобка
/x;
s/$levelN//g;
print $_;
Во второй альтернативе также нужно убрать фигурные скобки, т.к. в $levelN они уже присутствуют. На печати результат тот же:
{agff
Рассмотрим теперь случай, когда "скобки" представляют собой многосимвольные конструкции, например, теги <table и </table>. Раньше мы уже рассматривали такие "скобки" и выработали технический прием с заменой
Поставим задачу захватить из HTML-текста лишь самые внешние таблицы с их содержимым, а остальное отбросить. Искомая программа может выглядеть так:
$_=<<EOD;
aa<table>
<tr>
ff<table>
<tr>
</table>
<tr>
</table>bb
ssssssss
<table>
</table>
EOD
my $levelN;
$levelN=qr
"(?>
(?:(?!</?table).)+| # все символы до фрагмента </?table
<table[^>]*>(??{$levelN})</table> # или вся следующая таблица
)* # сколько угодно раз
"isx;
my @tables=m"<table[^>]*>$levelN</table>"gi;
print join "\n--\n",@tables if @tables;
Конструкция (?!</?table).)+ с помощью точки будет брать символы до встречи с фрагментом, соответствующим </?table. На печать выходит
<table> <tr> ff<table> <tr> </table> <tr> </table> -- <table> </table>
Я не стал перегружать это
По поводу использования динамических
$_='abc';
print "$1 $2" if /(a)(??{"(b)"})(c)/;
В результате напечатается
a c
Если бы мы попытались распечатать "$1 $2 $3", то на печать вышло бы то же самое и еще предупреждение об использовании неинициализированной переменной (это $3 ).
Если бы мы вставляли значение переменной, которое содержит текст (b), то результат был бы тем же:
$_='abc';
my $a='(b)';
print "$1 $2" if /(a)(??{$a})(c)/;
Вот еще аналогичный вариант с объектом
$_='abc';
my $a=qr'(b)';
print "$1 $2" if /(a)(??{$a})(c)/;
Но как только мы попытаемся внутри всего
Вот эти фрагменты кода вызывают ошибку
$_='abc';
my $a='(b)';
/(??{$a})/;
$_='abc';
print 'Found' if /(??{"(b)"})/;
$_='abc';
my $a=qr'(b)';
print 'Found' if /(??{"$a"})/;
Странно, но в случае использования объекта $a присутствует без кавычек, ошибки не возникает:
$_='abc';
my $a=qr'(b)';
print 'Found' if /(??{$a})/;
В остальных случаях
$_='abc';
my $a='(?:b)';
/(??{$a})/;
все варианты работают нормально. Поэтому не создавайте
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.