Пример поиска и подсветки ссылок и e-mail в тексте
Как же мы будем форматировать найденный текст? Одним оператором s///. Можно применить вариант с модификатором e, а можно обойтись без него, но тогда в выражении для замены надо будет использовать интерполяцию кода Perl в строку.
Этот оператор подстановки выглядит так:
$text =~ s!$re!<a href="${\($2 ? '' : 'http://')}
\L$1\E$3" target="_blank">$1$3</a>!gx;Регулярное выражение для поиска $re уже составлено, остается сформировать строку для замещения найденного URL. Мы не можем в этой строке замещения сделать конкатенацию вида "…".$var1."…".$var2…, потому что в нем участвует строка без символов-ограничителей строки. Поэтому уже знакомой конструкцией ${\( код Perl ) мы вставляем протокол с помощью тернарного оператора
$2 ? '' : 'http://'
Если протокол в URL был задан, то мы вставляем то, что задано, если не задан, то вставляем http://. Дальше вставляем часть URL без хвоста ($1), предварительно сделав в нем все буквы строчными. За ней идет хвост $3. А в тексте, что будет виден на HTML-странице, будет фигурировать то, что вводил пользователь: $1$3.
#!/usr/bin/perl -w
use strict;
my $protocol='(?:(?=[FfHh])(?i:http(?>s?)|ftp)://)';
my $host=<<HOST;
(?>[A-Za-z0-9]{1,63}\\.)
(?>[A-Za-z0-9]
(?>[-A-Za-z0-9]{0,62})\\.
)*
HOST
my $subdom=<<SUBDOM;
(?:
(?>[A-Za-z0-9]
(?:[-A-Za-z0-9]{0,61}[A-Za-z0-9])?
)\\.
)+
SUBDOM
my $wb='(?![A-Za-z0-9])';
my $zone=<<ZONE;
(?i:(?(?=[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)?
)
ZONE
my $port="(?::\\d{1,5}$wb)";
my $tail=<<TAIL;
(?:[/?]
(?>[^.,"'<>()[\\]{}\\s\\x7F-\\xFF]*)
(?:(?>[.,?]+)
(?:[^"'<>()[\\]{}\\s\\x7F-\\xFF]+)
)*
(?<![,.?!-])
)
TAIL
my $re=<<RE;
(
(?>($protocol)(?(2)(?>$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?)
RE
my $text=<<TEXT;
URLs:
Ftp://a.com/AAa
Look at:aaa.Museum.
http://www.proxy.com:80\@www.site.com/
http://proxy.com:80\@site.com/
http://proxy.com\@site.com/
aAaa.com.au.rr.ggg
Zwww.Yabcd.co.uk
Фforum.abcd.de
www.Abc.eu
П123.123.123.1234.com/?q=aaa
http://Abc.Tk
Ahttp://www.Abc.pt/AAa
http://abc.au/query/vid.cam.dig/sony.dcrhc15.htm#full_image
Ф.Www.old-avto.tk
NOT URLs:
aaa.museumm
http://aaa.museumm,
http://-aaa.com
www._aaa.com
www.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com
TEXT
$text =~ s!$re!<a href="${\($2 ? '' : 'http://')}\L$1\E$3" target="_blank">$1$3</a>!gx;
print $text;
Листинг
8.1.
А вот текст, который она печатает:
URLs: <a href="ftp://a.com/AAa" target="_blank">Ftp://a.com/AAa</a> Look at:<a href="http://aaa.museum" target="_blank">aaa.Museum</a>. <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.au.rr" target="_blank">aAaa.com.au.rr</a>.ggg <a href="http://zwww.yabcd.co.uk" target="_blank">Zwww.Yabcd.co.uk</a> Ф<a href="http://forum.abcd.de" target="_blank">forum.abcd.de</a> <a href="http://www.abc.eu" target="_blank">www.Abc.eu</a> П<a href="http://123.123.123.1234.com/?q=aaa" target="_blank">123.123.123.1234.com/?q=aaa</a> <a href="http://abc.tk" target="_blank">http://Abc.Tk</a> A<a href="http://www.abc.pt/AAa" target="_blank">http://www.Abc.pt/AAa</a> <a href="http://abc.au/query/vid.cam.dig/sony.dcrhc15.htm#full_image" target="_blank">http://abc.au/query/vid.cam.dig/sony.dcrhc15.htm#full_image</a> Ф.<a href="http://www.old-avto.tk" target="_blank">Www.old-avto.tk</a> NOT URLs: aaa.museumm http://aaa.museumm, http://-aaa.com www._aaa.com www.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.comЛистинг 8.2.
3-я, 10-я и 13-я строка не уместились по ширине страницы.
Обратите внимание, как преобразуется в URL строка
aAaa.com.au.rr.ggg
Получается
<a href="http://aaaa.com.au.rr" target="_blank">aAaa.com.au.rr</a>.ggg
.ggg не считается частью URL. Количество последовательностей символов через точку ограничено, чтобы не захватить в URL следующий за ним текст. Это интуитивное ограничение.
8.2 Преобразование ftp, http и e-mail ссылок в теги HTML
Если в тексте могут присутствовать адреса электронной почты, то наша задача усложняется, поскольку ссылка через прокси-сервер
http://www.proxy.com:80@www.site.com/
может трактоваться неоднозначно из-за наличия в ней символа @. Если мы сначала будем искать адреса электронной почты, то программа может "найти" такой e-mail:
80@www.site.com
Конфликт также может возникнуть со ссылками вида
ftp://login:passw@a-aa.com/www/
Чтобы устранить этот конфликт, перепишем регулярные выражения для поиска URL и добавим к ним регулярное выражение для поиска e-mail. Форматировать ссылки будем несколькими операторами подстановки, т.к. для одного оператора эта задача слишком сложна.
Вот текст всей этой программы:
#!perl -w
use strict;
my $wb='(?![A-Za-z0-9])';
my $protocol='(?:(?=[FfHh])(?i:http(?>s?)|ftp)://)';
my $host=<<HOST;
(?>[-A-Za-z0-9_]{1,63}\\.)
(?>[A-Za-z0-9_]
(?>[-A-Za-z0-9_]{0,62})\\.
)*
HOST
my $subdom=<<SUBDOM;
(?:
(?>[A-Za-z0-9]
(?:[-A-Za-z0-9]{0,61}[A-Za-z0-9])?
)\\.
)+
SUBDOM
my $subdom1='[A-Za-z0-9](?:[-A-Za-z0-9]{0,61}[A-Za-z0-9])?';
my $zone=<<ZONE;
(?i:
(?=[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)?
)
ZONE
my $port="(?::\\d{1,5}$wb)";
my $tail=<<TAIL;
(?:[/?]
(?>[^.,"'<>()\\[\\]{}\\s\\x7F-\\xFF]*)
(?:
(?>[.,?]+)
(?:[^"'<>()\\[\\]{}\\s\\x7F-\\xFF]+)
)*
(?<![,.?!-])
)
TAIL
my $firstchr='(?:[A-Za-z0-9])';
my $namechr='(?:[A-Za-z0-9_+.-])';
my $ip='(?:(?<!\\d)(?>\\d{1,3})\\.(?>\\d{1,3})\\.(?>\\d{1,3})\\.(?>\\d{1,3})(?!\\d))';
# Login и passw ограничены 32 символами
my $loginpasswat='(?:(?>[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://'; qq!<a href="$res\L$1\E$3" target="_blank">$1$3</a>!#gex;
# Оформляем ссылки с 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>"#gx;
# Оформляем е-мейлы. Этот оператор чувствителен к тексту, на который меняет предыдущие операторы!
s#((?<!$firstchr)$firstchr(?>$namechr{0,39})\@(?>$subdom1)(?:\.$subdom1)?\.$zone)
(?!(?>[^\s"<]*)(?:"\starget="_blank">|</a>))#<a href="mailto:$1">$1</a>#gx;
# Оформляем ссылки с IP
s#((?<![>/])$ip(?>$port?))($tail?)#"<a href=\"http://\L$1\E$2\"
target=\"_blank\">$1$2</a>"#gx;
print $_;
Листинг
8.3.
А вот результат ее работы:
<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>Листинг 8.4.
Как видим, в этом тестовом тексте программа правильно отделила e-mail ссылки от остальных ссылок.