≪
≫

Лекция 8. Пример поиска и подсветки ссылок и e-mail в тексте

В данной лекции рассматривается построение регулярных выражении для выделения ссылок в тексте в теги HTML.

Ключевые слова: perl, шаблон, подшаблон, класс символов, диапазон символов, метасимвол, эскейп-последовательность. обратная ссылка, альтернативный шаблон, квантификатор, минимальный квантификатор, числитель, повторитель, мнимый символ, якорь, модификатор, захватывающие скобки, незахватывающие скобки, сохраняющие скобки, нумерованная переменная, регулярное выражение.

При обработке таких текстов, как письма и сообщения на форумах, возникает задача отыскания в тексте ссылок и адресов электронной почты и выделение их в теги <а href=.

Преобразование FTP- и HTTP-ссылок в теги HTML

Возьмем такой текст:

Зайдите на www.intuit.ru и посмотрите список курсов

Здесь текст www.intuit.ru является ссылкой, несмотря на отсутствие протокола http://, который подразумевается по умолчанию. В итоге наше регулярное выражение должно преобразовать этот текст к такому виду:

Зайдите на <а href-’http://ww.intuit, ru”

target='’_blank”>www.intuit.ru и посмотрите список курсов

Может быть и так. что ссылка не отделена пробелом от окружающих слов или после нее идет знак препинания (точка, запятая и т. д.) Желательно, чтобы регулярное выражение это учитывало и не включало такой знак в ссылку. И конечно, оно не должно совпадать там, где ему совпадать не следует. Неплохо было бы, если бы оно также форматировало текст ссылки href: протокол, домен и субдомены должны быть записаны строчными буквами. А сам текст, который будет виден на странице, должен оставаться таким, каким его ввел участник форума. Задача эта непростая и не формализуется. Критерием успешности регулярного выражения является то. как оно справляется с набором тестов, которые провоцируют его к несовпадению или совпадению не в тех местах.

Это регулярное выражение достаточно сложное и громоздкое, и мы будем создавать его по частям. Начнем с протокола.

Протокол может быть HTTP, HTTPS и FTP. Для его обнаружения создадим строковую переменную $protocoi.

my $protocol='(?:(?=[FfHh])(?!:http(?>s?)|ftp)://)’;

Если в тексте следующий символ Л/, Н или h. то этот подшаблон делает проверку следующих за ним символов и, если это протокол, поглощает его вместе с префиксом. Я взял весь шаблон для протокола в скобки, потому что в общем регулярном выражении у этого подшаблона может стоять квантификатор, который должен относиться ко всему этому подшаблону, а не к последнему его символу /.

Результирующий оператор подстановки у нас будет иметь модификатор л*, поэтому для имени хоста запишем такое регулярное выражение в свободном формате:

my $host=«HOST;

  • (?>[A-Za-zO-9]{1,63}.)
  • (?>[A-Za-zO-9]
  • (?>[-A-Za-zO-9]{0,62}).

)*

HOST

Эта запись соответствует последовательности имен, разделенных точками, или ІР-адресу, она в том числе поглотит префикс www. Также учтено, что длина одного имени (от точки до точки) не может быть больше 63 символов. Обратите внимание на два обратных слэша перед точками. Подобная запись содержимого переменной (here doc) транслируется как строка в двойных кавычках. В таких строках обратный слэш является метасимволом. Поэтому, чтобы записать его в такой строке один раз, его надо повторить дважды. Если распечатать переменную $host, то мы увидим по одному обратному слэшу перед точками. По правде говоря. если бы мы поставили по одной обратной черте перед точками, то результат был бы тем же, потому что Perl игнорирует неизвестные эскейп-последователь-ности, такие как ., т. к. точка в строках не является метасимволом и оставляет обратную косую как она есть. Другое дело — сочетания $ и @. Тк. символы 5 и @ в строках, ограниченных двойными кавычками, являются префиксами имен переменных, Perl перед ними удаляет обратную косую черту, которая маскирует эти метасимволы.

Для поддоменов запишем такой шаблон: my $subdom=’(?:(?>[A-Za-zO-9](?:[-A-Za-zO-9J{0,61}[A-Za-zO-9])?).)+’;

Здесь строка ограничена апострофами. В таких строках есть только два метасимвола — обратный слэш и апостроф. Поэтому, если мы хотим вставить в строку, ограниченную апострофами, эти символы, то их надо замаскировать обратным слэшем: и ?. В результате мы вместо последовательности . получим во внутреннем представлении переменной $subdom последовательность .. что нам и нужно. Замечание относительно неизвестных эскейп-последовательностей для строк в двойных кавычках здесь также в силе.

Введем вспомогательный подшаблон, который будет повторяться в других подшаблонах:

my $wb=’(?![A-Za-zO-9])’;

Он означает, что справа не должно быть ни буквы, ни цифры.

Для зоны (это может быть сот, net, org, ru. de, name, museum, co.uk...) имеется такой подшаблон:

my $zone=«Z0NE;

  • (?i:(?(?=[a-z]{3}$wb)(?>comI net IorgIeduI biz|govIintI mil)I
  • (?(?=[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)”;

А после зоны может идти хвост, который содержит множество всяких параметров, передаваемых с URL:

my $tail=«TAIL;

  • (?:[/?]
  • (?>[".," 1<>()[]{}sx7F-xFF]*)
  • (?:(?>[.,?]+)

(?:["»’<>()[1{}sx7F-xFF]+)

)*

(?<’Г,•?!-])

)

TAIL

В конце стоит заглядывание назад:

(?

которое учитывает, что после URL могут стоять знаки препинания, которые в него не входят.

Все регулярное выражение для поиска URL в тексте выглядит немного страшновато:

my $re=«RE;

  • (?>($р гotocol)(?(2)(?>$host$zone)|$host$zone)
  • (?![A-Za-zO-9])|
  • (?-])
  • (?

$subdom$zone(?HA-Za-zO-9_. -]*@)

)

(?>(?>$port?(?>@$host$zone(?l[A-Za-z0-9_. -]*@))?)?)

)

($tail?)

RE

Это выражение учитывает заход через прокси-сервер вида

http://proxy.com^site.сот/

Часть URL от начала до хвоста, который может идти после символов /или ?, мы берем в нумерованную переменную $1. Эту часть URL внутри тега <а мы будем выводить маленькими буквами, а для отображения на странице будем выводить в том виде, в котором ее ввел автор сообщения. Протокол мы возьмем в переменную $2. Если протокола нет, то при форматировании ссылки мы подставим на его место текст http://. Хвост $tail мы захватываем в переменную $3.

Внутри переменной $ге встречаются эскейп-последовательности @. Но т. к. внутри текста here doc и @ являются метасимволами, то чтобы в результате получить последовательность @, надо написать @. Тогда при обработке такого текста превратится в , а @ превратится в и в конце получится нужная последовательность @. Для проверки напечатайте переменную $ге.

Программа должна «подсвечивать» ссылки в тексте. Например, имеем текст

Look at:ааа.Museum.

Это должно превратиться в

Look at:aaa.Museum.

Здесь возможный URL отделяется от прилипших справа и слева символов и оформляется в тег <а. Внутри строки Лге/имя хоста и зона записываются строчными буквами, а в тексте, который будет виден на странице, форматирования не производится. Еще программа должна учесть, что в хвосте URL (имена подкаталогов и параметры) форматировать текст нельзя, т. к. эти слова чувствительны к регистру символов.

Возьмем в качестве тестового такой текст:

my $text=«TEXT;

URLs:

Ftp://а.сот/ААа

Look at:ааа.Museum.

http://www.proxy,com: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

http://proxy.com: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

http:// Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

aAaa.com.au.rr.ggg

Zwww.Yahoo.co.uk

(Pforum.abcd.de

www.Abe.eu

П123,123.123.1234.com/?q=aaa

http://Abc.Tk

Ahttp://www.Abe.pt/AAa

http://abc.au/query/vid.cam.dig/sony.dcrhc15.htm#full_image

Ф.Www.old-avto. tk

NOT URLs:

aaa. museumin

http://aaa.museumm,

http://-aaa.com

www. aaa.com www.аааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааа.сот TEXT

Как же мы будем форматировать найденный текст? Одним оператором s///. Можно применить вариант с модификатором е, а можно обойтись без него, но тогда в выражении для замены надо будет использовать интерполяцию кода Perl в строку.

Этот оператор подстановки выглядит так:

$text =- s!$re!

ta r get=’'_blarik”>$ 1 $3! gx;

Регулярное выражение для поиска $ге уже составлено, остается сформировать строку для замещения найденного URL. Мы не можем в этой строке замещения сделать конкатенацию вида "... ”.$varl. "... ”.$Var2..., потому что в нем участвует строка без символов — ограничителей строки. Поэтому уже знакомой конструкцией ${( код Perl) мы вставляем протокол с помощью тернарного оператора

$2 ? “ : 'http://'

Если протокол в URL был задан, то мы вставляем то, что задано, если не задан, то вставляем http://. Дальше вставляем часть U RL без хвоста (57), предварительно сделав в нем все буквы строчными. За ней идет хвост $3. А в тексте, который будет виден на HTML-странице, будет фигурировать то, что вводил пользователь: $1$3.

Вот вся наша программа:

й!/usr/bin/perl -w

use strict;

my $protocol='(?:(?=[FfHh])(?i:http(?>s?)|ftp)://)’;

my $host=«HOST;

  • (?>[A-Za-zO-9]{1,63}.)
  • (?>[A-Za-zO-9]
  • (?>[-A-Za-z0-9]{0,62}).

)*

HOST

my $subdom=«SUBDOM;

(?:

  • (?>[A-Za-zO-9]
  • (?:С-A-Za-zO-9]{0,61}[A-Za-zO-9])?

)-

)+

SUBDOM

my $wb=’(?![A-Za-zO-9])’;

my $zone=«Z0NE;

  • (?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|(?l)

)

)

)

)

(?>.[a-z]{2}$wb)?

)

ZONE

my $port=”(?::d{1t5}$wb)”;

my $tail=«TAIL;

  • (?:[/?]
  • (?>["..”<>()[]{}sx7F-xFF]*)
  • (?:(?>[..?]+)
  • (?:["»'<>()[]{}sx7F-xFF]+)

)*

(?

)

TAIL

my $re=«RE;

  • (
  • (?>($protocol)(?(2)(?>$host$zone)|$host$zone)
  • (?![A-Za-zO-9])|
  • (?
  • (?

$subdom$zone(?![A-Za-z0-9_. -]*@)

)

(?>(?>$port?(?>@$host$zone(?! [A-Za-z0-9_. -]*@))?)?)

)

(Stall?)

RE

my $text=«TEXT;

URLs:

Ftp://a.com/AAa

Look at:aaa.Museum.

http://www.proxy.com: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

http://proxy,com: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

http:// Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

aAaa.com.au.rr.ggg

Zwww.Yabcd.co.uk

(Dforum.abcd.de

www.Abc.eu

П123.123.123.1234,com/?q=aaa

http://Abc.Tk

Ahttp://www.Abc.pt/AAa

http://abc.au/que ry/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!$1$3! gx;

print $text;

А вот текст, который она печатает:

URLs:

<а hrof=”ftp://a.com/AAa" target=”_blank”>Ftp://a.com/AAa

Look at:aaa.Museum.

http://www.proxy.com: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

http://

proxy.com: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

<а href=”http:// Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /’' target-’_blank”>http:// Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

http://aaaa.com.au.r r» ta rget="_blank”>aAaa.com.au.rr.ggg Zwww.Yabcd.co.uk Ф<а href="http://forum.abcd.de" target="_blank”>forum.abed.de www.Abe.eu

П<а href="http://123.123.123.1234.com/?q=aaa” target=”_blank»>123.

123.123.1234.com/?q=aaa

http://Abc.Tk

Ahttp://www.Abe.pt/AAa> 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

NOT URLs:

aaa.museumm

http://aaa.museumm,

http://-aaa.com

www._aaa.com

www.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com

3-я, 10-я и 13-я строка не уместились по ширине страницы.

Обратите внимание, как преобразуется в URL строка

aAaa.com.au.rr.ggg

Получается

<а href=”http://aaaa.com.au.гг» target-'_Ыапк”>аАаа. com.au. rr.ggg

.ggg не считается частью URL. Количество последовательностей символов через точку ограничено, чтобы не захватить в URL следующий за ним текст. Это интуитивное ограничение.

Преобразование FTP-, HTTP- и e-mail ссылок в теги HTML

Если в тексте могут присутствовать адреса электронной почты, то наша задача усложняется, поскольку ссылка через прокси-сервер

http://www.proxy.com: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script / может трактоваться неоднозначно из-за наличия в ней символа Если мы сначала будем искать адреса электронной почты, то программа может «найти» такой e-mail:

80@www. site.com

Конфликт также может возникнуть со ссылками вида

ftp://login: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /www/

Чтобы устранить этот конфликт, перепишем регулярные выражения для поиска URL и добавим к ним регулярное выражение для поиска e-mail. Форматировать ссылки будем несколькими операторами подстановки, т.к. для одного оператора эта задача слишком сложна.

Вот текст всей этой программы:

#!perl -w

use strict;

my $wb=’(?![A-Za-zO-9])’;

my $protocol='(?:(?=[FfHh])(?i:http(?>s?)I ftp)://)’;

my $host=<

  • (?>[-A-Za-zO-9_]{1,63}.)
  • (?>[A-Za-zO-9_]
  • (?>[-A-Za-z0-9_]{0,62}).

)*

HOST

my $subdom=«SUBDOM;

  • (?:
  • (?>[A-Za-zO-9]
  • (?: [-A-Za-z0-9]{0,61}[A-Za-zO-9])?

).

)+

SUBDOM

my $subdom1='[A-Za-zO-9](?:[-A-Za-zO-9]{0,61}[A-Za-zO-9])?’;

my $zone=«ZONE;

(?1:

  • (?=[a-z]{3}$wb)
  • (?>comI net I оrgIedu|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)7

)

ZONE

my $port=”(?::d{1t5}$wb)”;

my $tall=«TAIL;

  • (?:[/?]
  • (?>[“.,” ‘<>()[]{}sx7F-xFF]*)
  • (?:
  • (?>[.,?]+)
  • (?:["” ‘<>()[]{}sx7F-xFF]+)

)*

(?

)

TAIL

my $firstchr='(?:[A-Za-zO-9])';

my $namechr=’(?:[A-Za-zO-9_+.-])';

my $ip=’(?:(?d{1,3}).(?>d{1,3}).(?>d{1,3}). (?>d{1,3})(?!d))’;

tl Login и passw ограничены 32 символами

my $loginpasswat-'(?:(?>[A-Za-zO-9_]{1,32})(?>(?:: [A-Za-zO-9_]{1,32})?)@)';

my Ires;

$_=q(http://www.proxy.com:80@www. site.com/

Ftp://a.com/AAa

Ftp://Login:PasswaWww.Aaa.Com/Www/

Ftp://login:pas Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /www/

Ma1Ito: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script со.

Mailto: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script eee.co.

Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script biu@ddd. com bibi@ddbld. com

bibisbi-sf. f f. com. com@ddd. com

ыы$ы Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script

Mailto: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script ,

Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script mblblbl

aaa.Bb, Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script

aaa.museumm

Look at:aaa.museum.

httpS://aaa.museumm,

http://www.proxy.com: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

http://proxy.com: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

http://proxy,com@site,com/

aAaa.com.ru.rr.ggg

Zwww.Yabcd.co.uk

4>f о rum. abode. ru

www.Eabcd. ru

http://Eabcd.Ru

Ahttp://www.Eabcd.ru/AAa

http://abc.ru/que ry/vid.cam.dig/sony.dcrhc15.htmftfull_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

Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script

http://99.999.999.999/search?q=cache:w5K8GsupwvcJ:Olympus.flexiblesoft. com/c-4000-man.doc+c-4000-man&hl=ru&client=firefox-a );

tt Оформляем ссылки без login :passw s#((?>($protocol)(?(2)(?>$ip|$host$zone)|$host$zone)(?![A-Za-zO-9])|(?(?>$port?(?>@$host$zone(?![A-Za-z0-9_.-]*@))?)?))($tail?)#$res=$2 ? ‘' : ‘http://’; qq!$1$3!#gex;

# Оформляем ссылки c login:passw s#($protocol)($loginpasswat)($ip|$host$zone)((?>$port?)$tail?)#,’_blank,’>$1$2$3$4«#gx;

ft Оформляем е-мейлы. Этот оператор чувствителен к тексту, на ft который меняет предыдущие операторы!

s#((?$namechr{0,39})@(?>$subdom1)(?:.$su bdoml)?.$zone)(?! (?>[As»<]*)(?:"starget=,’_blank’,>|))ff$1#gx;

ft Оформляем ссылки c IP

s#((?/])$ip(?>$port?))($tail?)#»http://L$1E$2” target=’'_blank”>$1 $2«#gx;

print $_;

А вот результат ее работы:

<а href=’’http ://www.proxy.com: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /”

target="_blank”>http://www.proxy.com: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

Ftp: //a. com/AAa

Ftp://Login: Passw@Www. Aaa. Com/Www/"

Ftp://login: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /www/«

Mai Ito: aaa@sss. zzz. co.

MaiIto:aaa@sss. zzz.eee.co.

Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script

Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script

ыызы Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script bibisbi Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script MaiIto: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script ,

aaa@sss. сотЫЫЫ

aaa.Bb. Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script aaa.museumm

Look at:aaa.museum. https://aaa.museumm,

target="_blank”>http://www. proxy. com:80@www. site.com/

http://proxy, com: Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

http://proxy. Этот адрес e-mail защищен от спам-ботов. Чтобы увидеть его, у Вас должен быть включен Java-Script /

aAaa.com.ru.rr.ggg Zwww.Yabcd.co.uk Ф<а href="http://forum.abcde. ru” target=”_blank’'>forum. abode, ru

<а href=”http://www.eabcd.ru” target="_blank’,>www. Eabcd. ru

http://Eabcd.Ru

Ahttp://www. Eabcd.ru/AAa

http://abc.ru/query/vid. cam.dig/sony. dcrhc15.htm#full_ image

Ф.<а href=”http://www.abedefg-avto. ru» target="_blank">Www. abedefg-avto,ru

http://1.2.3.400/aaa/ddd.exe" target=,’_blank">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” target=”_blank">http://66.123. 234.555/ddd

mailto:michel@ab-cdefg. ru’’>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” target=’'_blank">http://99.999.999.999/sea rch?q=cache:w5K8GsupwvcJ: Olympus.flexiblesoft.com/c-4000-man.doc+c-4000-man&hl=ru&client= firefox-a

Как видим, в этом тестовом тексте программа правильно отделила е-mail ссылки от остальных ссылок.

 
Посмотреть оригинал