Построение эффективных регулярных выражений. Оператор qr/…/ и объекты регулярных выражений.
Есть еще одна проблема с модификатором o: если в подпрограмме объявляется переменная my и она используется в регулярном выражении с модификатором o, то при первом применении этого регулярного выражения, когда оно транслируется, оно привязывается к этому экземпляру интерполируемой переменной, который существовал на момент трансляции регулярного выражения. При последующих вызовах этой подпрограммы переменная my, которая интерполируется в регулярное выражение, каждый раз будет создаваться заново, но в регулярном выражении все равно будет использоваться ссылка на тот первый экземпляр этой переменной. Поэтому изменение этой внутренней переменной в подпрограмме не повлечет изменения регулярного выражения. Вот пример:
$_='ab'; &subr(); $_='cd'; &subr(); sub subr() { my $b=$_; print "\$b=$b\n"; if (/$b/) { print "Found $&\n" } else { print "Not found\n" } }
Выводится, как и положено,
$b=ab Found ab $b=cd Found cd
При первом обращении к подпрограме subr ее переменная $b имеет значение ab, при втором обращении она имеет значение cd. Пример без модификатора o работает так, как мы ожидали. Теперь поставим модификатор о к регулярному выражению:
$_='ab'; &subr(); $_='cd'; &subr(); sub subr() { my $b=$_; print "\$b=$b\n"; if (/$b/o) { print "Found $&\n" } else { print "Not found\n" } }
Печатается
$b=ab Found ab $b=cd Not found
Как говорится, что и требовалось доказать. Здесь дело даже не в том, что переменная $b объявлена в подпрограмме, она могла быть объявлена просто в каком-нибудь блоке, эффект был бы таким же:
$_='ab'; for my $i (0..1) { my $b=$_; print "\$b=$b\n"; if (/$b/) { print "Found $&\n" } else { print "Not found\n" } $_='cd'; }
Печатается
$b=ab Found ab $b=cd Found cd
А в случае с модификатором о:
$_='ab'; for my $i (0..1) { my $b=$_; print "\$b=$b\n"; if (/$b/o) { print "Found $&\n" } else { print "Not found\n" } $_='cd'; }
Печатается
$b=ab Found ab $b=cd Not found
Но если подобное регулярное выражение используется в подпрограмме многократно в цикле, то отсутствие модификатора о повлечет за собой многократную компиляцию этого регулярного выражения, хотя переменная $b внутри подпрограммы не меняет своего значения. Как тут быть? Эту проблему решают объекты регулярных выражений.
Объект регулярного выражения $re=qr/$b/ будет компилироваться каждый раз при входе в подпрограмму, но внутри цикла можно использовать этот объект многократно без перекомпиляции регулярного выражения:
$_='ab'; &subr(); $_='cd'; &subr(); sub subr() { my $b=$_; my $re=qr/$b/; print "\$b=$b\n"; if (/$re/) { print "Found $&\n" } else { print "Not found\n" } }
Печатает
$b=ab Found ab $b=cd Found cd
10.3. Применение объктов регулярных выражений
Объекты регулярных выражений можно применять для повышения эффективности и построения библиотек регулярных выражений, как "кирпичики". Вот пример уже рассмотренного поиска и подсветки ссылок и e-mail в тексте с использованием объектов регулярных выражений вместо интерполяции переменных:
#!perl -w use strict; my $wb=qr'(?![A-Za-z0-9])'; my $protocol=qr'(?=[FfHh])(?i:http(?>s?)|ftp)://'; my $host=qr'(?>[-A-Za-z0-9_]{1,63}\.) (?>[A-Za-z0-9_] (?>[-A-Za-z0-9_]{0,62})\. )*'x; # вместе с www my $subdom=qr'(?:(?>[A-Za-z0-9](?:[-A-Za-z0-9]{0,61}[A-Za-z0-9])?)\.)+'; my $subdom1=qr'[A-Za-z0-9](?:[-A-Za-z0-9]{0,61}[A-Za-z0-9])?'; my $zone=qr"(?(?=[a-z]{3}$wb)(?>com|net|org|edu|biz|gov|int|mil)| (?(?=[a-z]{2}$wb)[a-z]{2}| (?(?=[a-z]{4}$wb)(?>info|aero|name)| (?(?=[a-z]{6}$wb)museum|(?!) ) ) ) ) (?>\.[a-z]{2}$wb)?"ix; my $port=qr":\d{1,5}$wb"; my $tail=qr#[/?](?>[^.,"'<>()\[\]{}\s\x7F-\xFF]*) (?:(?>[.,?]+)(?:[^"'<>()\[\]{}\s\x7F-\xFF]+))*(?<![,.?!-])#; my $firstchr=qr'[A-Za-z0-9]'; my $namechr=qr'[A-Za-z0-9_+.-]'; my $ip=qr'(?<!\d)(?>\d{1,3})\.(?>\d{1,3})\.(?>\d{1,3})\.(?>\d{1,3})(?!\d)'; # Login и passw ограничены 32 символами my $loginpasswat=qr'(?>[A-Za-z0-9_]{1,32})(?>(?::[A-Za-z0-9_]{1,32})?)@'; my $res; $_=q(http://www.proxy.com:80@www.site.com/ Ftp://a.com/AAa Ftp://Login:Passw@Www.Aaa.Com/Www/ Ftp://login:passw@a-aa.com/www/ Mailto:aaa@sss.zzz.co. Mailto:aaa@sss.zzz.eee.co. aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@aaa.com ыы@ddd.com ыы@ddЫd.com ыыsы-sf.ff.com.com@ddd.com ыыsы.-sf.ff@ddd.com Mailto:aaa@sss.co, aaa@sss.comЫЫЫ aaa.Bb.b@aaaa.com.ru.rr.ggg aaa.museumm Look at:aaa.museum. httpS://aaa.museumm, http://www.proxy.com:80@www.site.com/ http://proxy.com:80@site.com/ http://proxy.com@site.com/ aAaa.com.ru.rr.ggg Zwww.Yabcd.co.uk Фforum.abcde.ru www.Eabcd.ru http://Eabcd.Ru Ahttp://www.Eabcd.ru/AAa http://abc.ru/query/vid.cam.dig/sony.dcrhc15.htm#full_image Ф.Www.abcdefg-avto.ru httP://1.2.3.400/aaa/ddd.exe? 1.2.3.400/aaa/ddd.exe?d=c,f=t;&e=h, .0.2.3.400. http://66.123.234.555/ddd michel@ab-cdefg.ru http://99.999.999.999/search?q=cache:w5K8GsupwvcJ:olympus.flexiblesoft.com/ c-4000-man.doc+c-4000-man&hl=ru&client=firefox-a ); # Оформляем ссылки без login:passw s#((?>($protocol)(?(2)(?>$ip|$host$zone)|$host$zone)(?![A-Za-z0-9])|(?<![A-Za-z0-9_\@-]) (?<!\.(?!(?i:www)))$subdom$zone(?![A-Za-z0-9_.-]*\@)) (?>(?>$port?(?>\@$host$zone(?![A-Za-z0-9_.-]*\@))?)?)) ($tail?)#$res=$2 ? '' : 'http://'; "<a href=\"$res\L$1\E$3\" target=_blank>$1$3</a>"#ge; # Оформляем ссылки с login:passw s#($protocol)($loginpasswat)($ip|$host$zone) ((?>$port?)$tail?)#<a href=\"\L$1\E$2\L$3\E$4\" target=_blank>$1$2$3$4</a>"#g; # Оформляем е-мейлы. Этот оператор чувствителен к тексту, на который меняет предыдущие операторы! s#((?<!$firstchr)$firstchr(?>$namechr{0,39})\@(?>$subdom1) (?:\.$subdom1)?\.$zone)(?!(?>[^\s"<]*) (?:" target=_blank>|</a>))#<a href="mailto:$1">$1</a>#g; # Оформляем ссылки с IP s#((?<![>/])$ip(?>$port?))($tail?)#"< a href=\"http://\L$1\E$2\" target=_blank>$1$2</a>"#g; print $_;Листинг 10.1.
<a href="http://www.proxy.com:80@www.site.com/" target=_blank>http://www.proxy.com:80@www.site.com/</a> <a href="ftp://a.com/AAa" target=_blank>Ftp://a.com/AAa</a> <a href="ftp://Login:Passw@www.aaa.com/Www/" target=_blank>Ftp://Login:Passw@Www.Aaa.Com/Www/</a>" <a href="ftp://login:passw@a-aa.com/www/" target=_blank>Ftp://login:passw@a-aa.com/www/</a>" Mailto:<a href="mailto:aaa@sss.zzz.co">aaa@sss.zzz.co</a>. Mailto:aaa@sss.zzz.eee.co. aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@aaa.com ыы@ddd.com ыы@ddЫ<a href="http://d.com" target=_blank>d.com</a> ыыsы-<a href="mailto:sf.ff.com.com@ddd.com">sf.ff.com.com@ddd.com</a> ыыsы.-<a href="mailto:sf.ff@ddd.com">sf.ff@ddd.com</a> Mailto:<a href="mailto:aaa@sss.co">aaa@sss.co</a>, <a href="mailto:aaa@sss.com">aaa@sss.com</a>ЫЫЫ <a href="mailto:aaa.Bb.b@aaaa.com.ru.rr">aaa.Bb.b@aaaa.com.ru.rr</a>.ggg aaa.museumm Look at:<a href="http://aaa.museum" target=_blank>aaa.museum</a>. httpS://aaa.museumm, <a href="http://www.proxy.com:80@www.site.com/" target=_blank>http://www.proxy.com:80@www.site.com/</a> <a href="http://proxy.com:80@site.com/" target=_blank>http://proxy.com:80@site.com/</a> <a href="http://proxy.com@site.com/" target=_blank>http://proxy.com@site.com/</a> <a href="http://aaaa.com.ru.rr" target=_blank>aAaa.com.ru.rr</a>.ggg <a href="http://zwww.yabcd.co.uk" target=_blank>Zwww.Yabcd.co.uk</a> Ф<a href="http://forum.abcde.ru" target=_blank>forum.abcde.ru</a> <a href="http://www.eabcd.ru" target=_blank>www.Eabcd.ru</a> <a href="http://eabcd.ru" target=_blank>http://Eabcd.Ru</a> A<a href="http://www.eabcd.ru/AAa" target=_blank>http://www.Eabcd.ru/AAa</a> <a href="http://abc.ru/query/vid.cam.dig/sony.dcrhc15.htm#full_image" target=_blank>http://abc.ru/query/vid.cam.dig/sony.dcrhc15.htm#full_image</a> Ф.<a href="http://www.abcdefg-avto.ru" target=_blank>Www.abcdefg-avto.ru</a> <a href="http://1.2.3.400/aaa/ddd.exe" target=_blank>httP://1.2.3.400/aaa/ddd.exe</a>? "<a href="http://1.2.3.400/aaa/ddd.exe?d=c,f=t;&e=h" target=_blank>1.2.3.400/aaa/ddd.exe?d=c,f=t;&e=h</a>", ."<a href="http://0.2.3.400" target=_blank>0.2.3.400</a>". <a href="http://66.123.234.555/ddd" target=_blank>http://66.123.234.555/ddd</a> <a href="mailto:michel@ab-cdefg.ru">michel@ab-cdefg.ru</a> <a href="http://99.999.999.999/search?q= cache:w5K8GsupwvcJ:olympus.flexiblesoft.com/c-4000-man.doc+c-4000-man&hl=ru&client=firefox-a" target=_blank>http://99.999.999.999/search?q=cache:w5K8GsupwvcJ:olympus.flexiblesoft.com/ c-4000-man.doc+c-4000-man&hl=ru&client=firefox-a</a>Листинг 10.2.
Если регулярное выражение применяется неоднократно, то в качестве его "кирпичиков" лучше использовать объекты регулярных выражений, чем каждый раз интерполировать в него переменные, т.к. объекты регулярных выражений компилируются, только когда в программе выполняется оператор qr/…/.