Опубликован: 18.06.2007 | Доступ: свободный | Студентов: 1391 / 36 | Оценка: 4.14 / 3.29 | Длительность: 12:44:00
ISBN: 978-5-94774-604-4
Лекция 10:

Построение эффективных регулярных выражений. Оператор qr/…/ и объекты регулярных выражений.

< Лекция 9 || Лекция 10: 123456 || Лекция 11 >

Есть еще одна проблема с модификатором 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/…/.

< Лекция 9 || Лекция 10: 123456 || Лекция 11 >
Константин Бражников
Константин Бражников
Россия
Mike .
Mike .
Россия