Регулярные выражения в perl · Регулярные выражения в perl...

54
Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали это произведение, нам ничего Спасибо нашим читателям, благодаря им мы нашли фамилию автора Но по-прежнему остается неизвестным герой, который перевел его на нибудь именно об этом переводе (существуют другие), пожалуйста, Тихоном Тарнавским aka t.t. Определения одиночные символы (characters) классы символов (character classes) альтернативные шаблоны (alternative match patterns) квантификаторы (quantifiers) мнимые символы (assertions) ссылки на найденный текст (backreferences) Функции, использующие регулярные выражения split grep map другие Как работают регулярные выражения Логические операции в регулярных выражениях Вызов функций и подпрограмм Использование встроенных переменных Примеры Рабочие программы, использующие регулярные выражения Выделение чисел в математической записи Облегчение поиска работы Очень простое решение для зеркала новостной ленты

Upload: others

Post on 12-Aug-2020

13 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

РегулярныевыражениявPerl

СтивХолзнер

От редактора: Когда мы публиковали это произведение, нам ничего не было известно об его авторстве.Спасибонашимчитателям,благодаряиммынашлифамилиюавтораоригинальноготекста(СтивХолзнер).Нопо-прежнемуостаетсянеизвестнымгерой,которыйперевелегонарусскийязык.ЕслиВамизвестночто-нибудь именно об этомпереводе (существуютдругие), пожалуйста,ТихономТарнавскимakat.t.

Определенияодиночныесимволы(characters)классысимволов(characterclasses)альтернативныешаблоны(alternativematchpatterns)квантификаторы(quantifiers)мнимыесимволы(assertions)ссылкинанайденныйтекст(backreferences)

Функции,использующиерегулярныевыраженияsplitgrepmapдругие

КакработаютрегулярныевыраженияЛогическиеоперацииврегулярныхвыраженияхВызовфункцийиподпрограммИспользованиевстроенныхпеременныхПримерыРабочиепрограммы,использующиерегулярныевыражения

ВыделениечиселвматематическойзаписиОблегчениепоискаработыОченьпростоерешениедлязеркалановостнойленты

Page 2: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Выводрезультатовпоиска

Определения

Регулярные выражения в perl одна из самых мощных его возможностей. Они позволяют сопоставлять текст суказаннымшаблоном,разбиватьтекствмассивпошаблону,производитьзаменутекстапошаблонуимногоемногоедругое.Так-жеиногдарегекспаминазываютсяоператорыпоискаизамены.

Оператор q(text) заменяет строку text на строку, заключенную в одинарные кавычки(например если впоставитьсимволq(text\n),тонапечатаетtext\n,т.е.\nэтодвасимвола,подобноpamam$file).Вданномслучаепочтивсеспециальныесимволынебудутинтерпретироватьсявнутри'\'

$some=q(Don'tmaybe);

Оператор qq~text~; (вместо значка ~ можно ставить например знакмногострочнымитекстами.пользуясьэтимоператоромможновыводитьцелыекускиhtml-кодаиписатьвэтомкодеименаскалярныхпеременных.

Операторqw("text")разбиваетстрокунамассивслов.

@mass=qw("явышелпогулятьиувиделкакчерезрекустроятновыймост");

#хотяснастроеннойлокальюбудетработатьи

@mass=qw(явышелпогулятьиувиделкакчерезрекустроятновыймост);

for(@mass){print$_,"\n"}

Операторqr/pattern/ключи-imosxработаетподобнорегулярномувыражению

$rex=qr/my.STRING/is;

s#$rex#foo#;

#тожесамое,чтои

s/my.STRING/foo/is;

Результатможетиспользоватьсяподобновызовуподпрограммы(смperldocperlopRegexquotelikeoperator)

$re=qr/$pattern/;

$string=~/foo${re}bar/;

$string=~$re;

$string=~/$re/;

Page 3: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Ключиimosxстандартные(см.ниже)

Оператор qx/STRING/ работает как системная команда, подобноиллюстрирующаяиспользованиеданногооператора:

#!/usr/bin/perl

qx[dbfdump--fs="\x18"--rs="\x19"pdffile.dbf>pdffile.txt];

файл pdffile.dbf содержит memo-поля(memo-поле содержит ссылку, подобно функциирасширением *.fpt), которые при помощи DBI.pm мне когда-то давно выудить не удалось. Принимает разрешенияFoxBASE4идампитфайлысовстроеннымиmemo-полямивтекстовыйвид.Т.е.такимобразомполучилосьвытащитьинформациюизфайлаmemo-типа*.fpt.

Допустимиспользуякоманду$perl_info=qx(ps$$);мывыводиминформациюотекущемпроцессезапущенногоскрипта(каждая запущенная программа в UNIX имеет свой собственный уникальный идентификатор, которыйсодержится во встроеннойпеременной$$ - достаточно уникальное число,можноиспользоватьпочти как счетчикслучайных чисел). Если сказать $shell_info = qx'ps $$'; то выведет информацию о самомосуществляютсвоеобразноеэкранированиеотдвойнойкавычки.

Вперлестьтриосновныхоператора,работающихсостроками:

m/.../-проверкасовпадений(matching),s/.../.../-подстановкатекста(substitution),tr/.../-заменатекста(translation).

Опертаорm/.../анализируетвходнойтекстиищетвнемподстрокусовпадающуюсуказаннымшаблоном(онзаданрегулярнымвыражением).Операторs/.../.../выполняетподстановкуоднихтекстовыхфрагментоввместодругих,припомощирегулярныхвыражений.Операторtr/.../.../заменяетвыходнойтекст,ноприэтомоннеиспользуетрегулярныевыражения,осуществляязаменупосимвольно.

Оператор m/шаблон/ - поиск подстроки по определенному шаблону. Например{4})!gнайдетивыведетвседатывпеременной$_.Вшаблоненеважно,чтобудетегоограничителем.Напримерприпоиске гиперссылок, которые зачастую содержат символы/, разумнее пользоваться несимволамиограничителями.Втакомслучаешаблонбудетболеепростдляпониманиядругимпрограммистам,даинемного короче. В perl оператор m/.../ используется очень часто, и поэтому используется сокращение, безначальной буквы m. Если начальная буква есть, то в качетсве символов ограничителейможно исползовать любойдругойсимвол.

Дляоператораm/pattern/есть6параметров:gimsxo

Page 4: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

m/foo/g говорит компилятору найти все foo в тексте, в то время какподстроки foo в строке $_. В строке $_ содержится обычный текст, как и в переменнойпеременная,толькоонасуществуетвсегдаивводится,когданеопределенаспециальнодругая,поумолчанию.

Напримерможносказатьfor(@mass){print$_,"\n"}илиfor$elem(@mass){print$elem,"\n"}делаютодноито-же,новпервомслучаезаписькороче,даизачастуюбываетудобноиспользоватьпеременнуюнапример,когданужновыделитьприпомощирегулярноговыраженияопределенныеданные,пользуясьперебороммассива(функцияmap):

@res=map{/(\d\d\d\d)/}split/\s/,$texts;

чтоэквививалентнокоду

push@res,$1whilem!((\d){4})!g;#(вданномслучае$_=$texts)

иличтоэквивалентноконструкции

foreach(split/\s/,$texts){

push@res,$1if(/(\d\d\d\d)/g)

}

Следующийпараметрm/foo/i,говоритотом,чтоненужноучитыватьрегистрприпоискепоподстроке.

Параметрm/foo/sговоритоттом,чтострока,покоторойпроизводитсяпоиск,состоитизоднойстрочки.

Напримернужновыцепитьвсеurlкартинокизстраничкиwww.astronomynow.com,чтобысделатьлокальноезеркалоэтойстраничкиипользователимоглисинтересомчитатьпоследниеновостиастрономии:

#!/usr/bin/perl-wT

useLWP::Simple;

$page=get"http://www.astronomynow.com";

&getlink($page);

subgetlink{

local$_=$_[0];

push(@res,"http://$2")

whilem{SRC\s*=\s*(["'])http://(.*?)\1\s*(.*?)WIDTH="100"HEIGHT="100"(.*?)>}igs

}

В подпрограмме заводится при помощи функции local переменная, видимая только в области действияподпрограммы. Этой переменной присваивается значение переменной

Page 5: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

выкачаннойSimple.pmстранички.

Можносделатьнемногоподругому,сохранитьскачаннуюстраничкувфайлнадискизатемследующее:

$/="\001";

openF,"<page.html";$page=<F>;closeF;

&getlink($page);...

Встроеннаяпеременная$/содержитсимволразделителявходныхзаписей.Этоможетбытьпереводкареткиили,приuploadfar'омнасерверфайловвнеASCIвиде,онаприобретаютнаконцестрочкихитрыйсимвол

Если $/ переопределить, то можно свободно пользоваться дескрипторами открытия файлов для просмотрамногострочного текста(m/pattern/s). Например когда открывается файл при помощи функции<file.txt";@mass=<F>, топрисваиваядескрипторF массиву вмассиве появятся строчки, разделенные символом,содержащимсяв$/.

Переопределив$/можнозапростонаписать:

openF,"<file.txt";$mass=<F>

ивпеременной$massбудетсодержатьсямногострочныйтекстсточкизрениячеловека,нопрограммабудетвидетьэтот текст как одну строку и по тексту можно будет запросто пройтись поискомнеобходимыеподстроки.

Параметрm/foo/oговоритоттом,чтошаблоннужнокомпилироватьтолькоодинраз.Еслиоператориспользуетсявсочетании с операциями привязки =~ и отрицание !~, то строкой, в которой ведется поиск, является переменная,стоящаяслеваотоперациипривязки.Впротивномслучаепоискведетсявстроке

Операторs!pattern!substring!-поисквстрокепошаблонуpatternизаменанайденноготекстанаи для оператора m/.../, косую черту можно не ставить, пригоден любой символ, который не находится впротиворециисзаданнымвыражением.Нерекомендуетсяиспользоватьвкачествеограничителей

s!/usr/local/etc/!/some/where/else!-заменяетпуть.s(/usr/local/etc/)(/some/where/else)g-заменяетвсевстречающимесяпутидофайла.

параметры:egimsxoe-указывает,чтоsubstringнужновычислить.

напримернужнопеределатьвсеescapeпоследовательности,дляэтоговызываетсясоответствующаяподпрограмма:

Page 6: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

$text=~s/(&.*?;)/&esc2char($1)/egs;

т.е.изрегулярноговыраженияпроисходитвызовподпрограммы.

g-заменитьвсеодинаковыекомпоненты,анеодин,каквотсутствииключаi-неучитыватьрегистр.m-строка,вкоторойпроисходитпоиск,состоитизмножествастрок.s-строка,вкоторойпроисходитпоиск,состоитизоднойстроки.x - сложныйшаблон, т.е.можнописатьневстрочку, адляупрощенияпониманияразбиватьшаблоннанесколькострок,примерыобэтомниже.o-компилироватьшаблонодинраз.

Допустимнужносделатьпоисковик,которыйходитподиректориямнасервере,нонекоторыедиректориитипаbin/ит.п.индексироватьнельзя.Объявляемпеременную,котораябудетсодержатьрегулярноевыражение,вданномслучаеперечислениеилиimgилиimageилиtempилиtmpилиcgi-bin:

$no_dir='(img|image|temp|tmp|cgi-bin)';

Ключирегулярноговыраженияm#$no_dir$#ioговорятотом,чтокомпилироватьсодержимоеодинраз(ключo)итакжеещенеучитыватьрегистр(ключi).

Операторtr/выражение1/выражение2/,ключиcds

Смысл:заменавыражения1навыражение2.Еслиуказанключс,тоэтоинверсиявходятсодержащиесявнемсимволы.еслиуказаключd,тозначитстеретьзамененныесимволы.Еслиуказанключтозначитзаменитьмногочисленныеповторяющиесясимволынаодиночныйсимвол.

Операторy/выражение1/выражение2/(ключиcds),равносиленоператоруtr

Напримервпоисковойсистеменужноприводитьзапросвнижнийрегистр,дабынезависетьотнастроеклокали:

$CAP_LETTERS='\xC0-\xDF\xA8';

$LOW_LETTERS='\xE0-\xFF\xB8';

$code='$html_text=~';

$code.="tr/A-Z$CAP_LETTERS/a-z$LOW_LETTERS/";

$down_case=eval"sub{$code}";

одиночныесимволы

Page 7: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Врегулярномвыражениилюбой символ соответствует самому себе, если только онне являетсяметасимволом соспециальнымзначением (такимиметасимволамиявляются\,|,(,),[,{проверяется,неввеллипользователькоманду"quit"(иеслиэтотак,топрекращаемработупрограммы):

while(<>){

if(m/quit/){exit;}

}

Правильнее проверить, что введенное пользователем слово "quit" не имеет со-седних слов, изменяющих смыслпредложения.(Например,программавыполнитзаведомоневерноедействие,есливместо"quit"пользовательвведеткоманду "Don't quit!".) Это можно сделать с помощью метасимволовнечувствительнокразницемеждупрописнымиизаглавнымибуквами,используеммодификатор

while(<>){if(m/^quit$/i){exit;}}

Кромеобычныхсимволовperl определяет специальныесимволы.Онивводятся спомощьюобратнойкосойчерты(escape-последовательности)итакжемогутвстречатьсяврегулярномвыражении:

\077-восьмеричныйсимвол,\а-символBEL(звонок),\с[-управляющиесимволы(комбинацияCtrl+символ,вданномслучаеэтоуправляющийсимволESC),\d-соответствуетцифре,\D-соответствуетлюбомусимволу,кромецифры,\е-символescape(ESC),\Е-конецдействиякоманд\L,\Uи\Q,\f-символпрогонастраницы(FF),\1-следующаялитерастановитсястрочной(lowercase),\L-всепоследующиелитерыстановятсястрочнымивплотьдокомандй\Е,\n-символновойстроки(LF,NL),\Q-вплотьдокоманды\Евсепоследующиеметасимволыстановятсяобычнымисимволами,\r-символпереводакаретки(CR),\s - соответствует любомуиз "пробельных символов" (пробел, вертикальная , или горизонтальная табуляция,символновойстрокиит.д.),\S-любойсимвол,кроме"пробельного",\t-символгоризонтальнойтабуляции(НТ,TAB),\u-следующаялитерастановитсязаглавной(uppercase),\U-всепоследующиелитерыстановятсязаглавнымивплотьдокоманды\v-символвертикальнойтабуляции(VT),

Page 8: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

\w-алфавитно-цифровойсимвол(любаябуква,цифраилисимволподчеркивания),\W-любойсимвол,кромебукв,цифрисимволаподчеркивания,\x1B-шестнадцатиричныйсимвол.

Bat также можете "защитить" любой метасимвол, то есть заставить perl рассматривать его как обыкновенныйсимвол,анекаккоманду,поставивпередметасимволомобратнуюкосуючертутипа\w,\d и\s, которые соответствуютне одному, а любому символуиз некоторой группы.Также заметьте, чтоодинтакойсимвол,указанныйвшаблоне,соответствуетровноодномусимволупроверяемойстроки.Поэтомудлязаданияшаблона,соответствующего,например,словуизбукв,цифрисимволовподчеркивания,надоиспользоватьконструкцию\w+,какэтосделановследующемпримере:

$text="Hereissometext."

$text=~s/\w+/There/;

print$text;

Thereissometext.

классысимволов

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

$text="Hereisthetext.";

if($text="/[aeiou]/){print"Vowels:wegot'em.\n";}

Vowels:wegot'em.

Другой пример: с помощью шаблона [A-Za-z]+ (метасимвол + означает утверждение: "один или более такихсимволов")ищетсяизаменяетсяпервоеслово:

$text="Whatisthesubject.";

$text="s/[A-Za-z]+/Perl/;

print$text;

Perlisthesubject;

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

$text="perlisthesubjectonpage493ofthebook.";

Page 9: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

$text=-s/[a-Za-z\s]+/500/;

print$text;

perlisthesubjectonpage500ofthebook.

альтернативныешаблоны

Вы можете задать несколько альтернативных шаблонов, используя символшаблоны позволяют превратить процедуру поиска из однонаправленного процесса в разветвленный: если неподходит один шаблон perl подставляет другой и повторяет сравнение, и так до тех пор, пока не иссякнут всевозможныеальтернативныекомбинации.Например,следующийфрагментпроверяет,неввеллипользователь"exit","quit"или"stop":

while(<>){

if(m/exit|quit|stop/){exit;}

}

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

В следующем примере метасимволы ^ и $ обозначают начало и конец строки и отделяются от набораальтернативныхшаблоновспомощьюскобок:

while(<>){

if(m/^(exit|quit|stop)$/){exit;}

}

Альтернативные варианты перебираются слева направо. Как только найдена первая альтернатива, для которойвыполняется совпадение с шаблоном, перебор прекращается. Участки шаблона, заключенные в круглые скобки,выполняютспециальнуюрольпривыполненииоперацийпоискаизамены.Еслисимволскобках, он интерпретируется как обычный символ. Поэтому если вы используете конструкцию шаблона вида[Tim|Tom|Tam], то она будет эквивалентна классу символов [Tioam|]метасимволов и команд, специфичных для регулярных выражений - в частности, квантификаторы и мнимыесимволы,описанныевдвухпоследующихразделах,-внутриквадратныхскобокпревращаютсявобычныесимволыилиescape-последовательноститекстовыхстрок.

квантификаторы

Квантификаторыврегулярныхвыражениях

Page 10: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

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

$text="HellofromPeeeeeeeeeeeeeeerl.";

$text=~s/e+/e/:

print$text;

Hellofromperl.

мнимыесимволы

Мнимыесимволыврегулярныхвыражениях

В perl имеются символы (метасимволы), которые соответствуют не какой-либо литере или литерам, а означаютвыполнениеопределенногоусловия (поэтомуванглийскомязыкеихназываютassertions,илиутверждениями).Ихможно рассматривать как мнимые символы нулевого размера, расположенные на границе между реальнымисимволамивточке,соответствующейопределенномуусловию:

^-началострокитекста,$-конецстрокиилипозицияпередсимволомначалановойстроки,распо-ложенноговконце,\b-границаслова,\В-отсутствиеграницыслова,\А-"истинное"началостроки,\Z-"истинный"конецстрокиилипозицияпередсимволомначалановойстроки,расположенногов"истинном"концестроки,\z-истинныйконецстроки,\G-граница,накоторойостановилсяпредыдущийглобальныйпоиск,выполняемыйкомандой(?= шаблон) - после этой точки есть фрагмент текста, который соответствует указанному регулярномувыражению,(?!шаблон)-послеэтойточкинеттекста,которыйбысоответствовалуказанномурегулярномувыражению,(?<=шаблон)-передэтойточкойестьфрагменттекста,соответствующийуказанномурегулярномувыражению,(?<! шаблон) - перед этой точкой нет фрагмента текста, соответствующего указанному регулярномувыражению.

Например,воткаквыполнитьпоискизаменуслова,используяметасимволыграницыслов:

$text="Hereissometext.";

$text=s~/\b([A-Za-z]+)\b/There/;

print$text;

Page 11: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Thereissometext.

perlсчитаетграницейсловаточку,расположеннуюмежду\wи\W,независимооттого,вкакомпорядкеследуютэтисимволы.Вследующемпримеревыводитсясообщениеотом,чтопользовательввелслово"yes",приусловии,чтооноединственное,чтоввелпользователь.Дляэтогошаблонвключаетмнимыесимволыначалаиконцастроки:

while(<>){

if(m/^yes$/){

print"Thankyouforbeingagreeable.\n";

}

}

Приведенныйвышепримертребуеткомментария.Преждевсего,бросаетсявглазаналичиедвухгруппметасимволовдляначалаиконцастроки.Вбольшинствеслучаевониозначаютодноитоже,таккакобычносимволыновойстроки(тоесть\n),встречающиесявнутритекстовоговыражения,нерассматриваютсякаквложенныестроки.Однакоеслидля команды m/.../ или s/.../.../ указан модификатор m, то текстовое выражение будет рассматриваться какмногострочныйтекст, в котором границамистроквыступают символыновой строкитекста метасимвол ^ сопоставляется с позицией после любого символа новой строки, а не только с началомтекстового выражения. Точно также метасимвол $ - это позиция перед любым символом новой строки,расположеннымвнутритекстовоговыражения,анеобяательноконецтекстовоговыраженияилижепозицияпередконцевымсимволом\n.Однакометасимвол\A-началотекстовоговыражения,аметасимволвыра-жения или позиция перед концевым символом \n, даже если в текстовом выражении имеются вложенныесимволы \n и при выполнении операции поиска или йены указан модификаторсоответствуетлюбомусимволу,кромесимволановойстроки\n.Независимооттого,заданлимодификаторбудет сопоставляться ни c внутренними, ни с концевыми символами \nрассматривать\nкакобычныйсимвол-использоватьмодификаторs.

Отсюда понятна разница между метасимволами \Z и \z. Если в качестве текстового выражения используетсярезультатчтениявходногопотокаданных,тосбольшойвероятностьюданноевыражениезаканчиваетсясимволом\n,заисключениeмтоговарианта,когдапрограммапредусмотрительно"отщипнула"егоспомощьюфункцииилиchomp.Метасимвол \Z игнорирует концевой символ \n если он случайно остался на месте, рассматривая обеситуациикак "конецстроки".Вотличиеотнегометасимвол\z оказываетсяболеепунктуальнымирассматриваетконцевой символ \n как неотъемлемую часть проверяемого текстового выражения, если только пользователь непозаботилсяобудаленииэтогосимвола.

Отдельно следует остановиться на метасимволе \G. Он может указыватьсяв регулярном выражении только в томслучае, есливыполняется глобальныйпоиск (то есть есликомандаm/.../указанныйвшаблоне,соответствуетточке,накотбройостановиласьпредыдущаяоперацияпоиска.

ссылкинанайденныйтекст

Page 12: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Иногда нужно сослаться на подстроку текста, для которой получено совпадение с некоторой частью шаблона.Например, при обработке файла, HTML может потребоваться выделять фрагменты текста, ограниченныеоткрывающими и закрывающими метками HTML (например, <А> и </А>). В начале уже приводился пример, вкоторомвыделялсятекст,ограниченнуйметкамиHTML<А>и<B>.Следующийпримерпозволяетвыделятьтекст,расположенныймеждулюбымиправильнозакрытымиметками:

$text="<А>Hereisananct1or.</А>";

if($text=~m%<([A-Za-z]+)>[\w\s\.]+</\1>%i){

}

Вместо косой черты в качестве ограничителя шаблона использован другой символ. Это позволяет использоватьсимволкосойчертывнутришаблонабезпредшествующейемуобратнойкосойчерты.Каждомуфрагментушаблона,заключенномувкруглыескобки,соответствуетопределеннаявнутренняяпеременная.Переменныепронумерованы,такчтонанихможноссылатьсявнутришаблона,поставивпередномеромобратнуюкосуючерту(значенияпеременныхможноссылатьсявнутришаблона,какнаобычныйтекст,поэтомуесли открываю-щей меткой служит<А>, и , если открывающей меткой служит . Эти же самые внутренниепеременныеможноиспользоватьивнешаблона,ссылаясьнанихкакнаскалярысименами

$text="Ihave4apples.";

if($text=-/(\(\d+)/){

print"HereIsthenumberofapples:$1.\n";

Hereisthenumberofapples:4.

Каждой паре скобок внутри шаблона после завершения операции поиска будет соответствовать скалярнаяпеременная с соответствующим номером. Это можно 'использовать при выделении нужных для последующейработы фрагментов ана-лизируемой строки. В следующем примере мы изменяем порядок трех слов в тек-стовойстрокеспомощьюкомандыs/.../.../:

$text="Iseeyou.";

$text=-s/^(\w+)*(\w+)*(\w+)/$3$2$1/;

print$text;

youseeI.

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

$text="ABCDEFGH";

$text=-m/(\w(\w)(\w))((\w)(\w))/;

print"$1/$2/$3/$4/$5/$6/";

ABC/B/C/DE/D/E

Page 13: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Кромепеременных,ссылающихсянанайденныйтекст,можноиспользоватьспециальныепеременныеperl.

Функции,использующиерегулярныевыражения

Фактически,естьтрифункции,которыевкачестверазделителямогутиспользоватьрегулярныевыражение:grep,map и ещеможновоспользоваться специальнымиоператорами... иусловиямиif,unlessипростологическимиоператорами.

split

ЕслинеобходиморазделитьданныеизSTDINпонужномуразделителю,томожновоспользоватьсялокализацией

subexample_local{

local$/=undef;

@mass=split/pattern/,<>;

return1;

}

printscalar(@mass);

Можноразделятьданныеизфайлаитак:

undef$/;

@res=split/pattern/,<F>;

чтоэквивалентно:

while(<F>){push@asdf,split}

Послеsplitможноставитьвместозапятойистрелочку:

@mass=split/(\d){4}/=>$file;

Вфункции сплит можно воспользоваться макисмальным квантификаторомпозволитразделитьстрокунасимволы,которыхтамнет(всилутого,что*

@ruru=split/\001*/=>"lalalalalala";

#массив@ruruбудетсодержатьэлементыпооднойбукве.

Еслистрокасостоитизнесколькихстрок,томожнопоставитьразделителемисимволначалановойстроки:

$str="asdf\nghjk\nqwer\n";

@lines=split/^/=>$str;

Page 14: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Вобщем,вsplitможновставлятьлюбойпоискпошаблону.

grep

Функцияgrepтак-жепозволяетзапонятьмассивзначениями.Напримернужнополучитьсписокрасширенийфайловвзаданнойдиректории:

while(<$dir/*.*>){push@files,$_}#читаемдиректорию

@test=grep{s|.*/(.*?)\.(.*)|$2|}@files;#оставляемвдиректориитолькорасширенияфайлов

можноиспользоватьпризнакчетностидлязанесениявмассив:

@test1=qw(123456789);

@evens=grep($_%2==1)@test1;

Илиболеесложноерегулярноевыражениедлявытаскиваниявсехe-mailадресовизтекстовойстранички:

@mass=grep{s/(.*)([\w+\-\.]+\@[\w\-\.]+\.\w{2,3})(.*)/$2/ig}split/\n/,$test;

Здесьиспользуетсяукороченнаязапись:

@mass=grep{/pattern/}split/\n/,$test;

котораяэквивалентазаписииздвухсторчек:

@uuu=split/\n/,$test;

@mass=grep{/pattern/}@uuu;

map

Функция map похожа по своей работе на обычное условие if, допустим нужно разделить записи на блоки,разделенныечетырьмяпробелами:

@probel=mapm!\s{4}!,split/\n/,$test;

other

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

if(/pattern1/i../pattern2/i){...}

Page 15: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

#истинностьпервогооператоравключаетконструкцию,авторогоевыключает.

if($nomer1..$nomer2){...}

...невозвратитистину,вотличииот..,еслиусловиявыполняютсяводнойстроке.

if(/pattern1/i.../pattern2/i){...}

if($nomer1...$nomer2){...}

длямногострочногофайла

print-ne'printif3..15'file.txt

выведетстрокифайлас3по15строчку,та-жесамаяопреациянонемногоподругому:

openF,"<file";

while(<F>){

printif(3..15)

}

или с какой нибудь начальной и конечно разметкой, например есть вспомогательный файл шаблонов(просторазличныевидыhtml, в зависимостиотдействияпользователя) дляразныхопределенныслучаев, которыенужныисходяизконтекстапрограммы:

openF,"<file";

while(<F>){

printif(/<!--beginwelcome-->/i.../<!--endwelcome-->/i)

}

Такаяконструкцияпозволяетвыводитькускимногострочногоhtmlкода(дляоднострочногонужноставитьоператор..).Условиявтакихоператорахможноставитьиразнотипными

$file=qr/2345/;

while(<F>){

printif(/^$/..10);#увидим,чтонаходитсяотпустойдо10-йстроки

printif(/^\001/../$file/);#выведетвсе,чтопосленуляидотогочтозаданоqr

}

Программачтенияпочтовыхадресовизmboxилиsent-mail:

while(<F>){

nextunless/^From:?\s/i../^$/;

while(/([^<>(,;)\s]+\@[^<>(,;)\s]+)/)g{

print"$1\n"unless$test{$1}++;

}

}

запускается./regex.pl/root/mail/sent-mailивыводиткаждыйемейлпоодномуразу.

Page 16: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Использованиевстроенныхпеременных

$'-подстрока,следующаязасовпадением.$&-совпадениесшаблономпоиска$`-подстрока,расположеннаяпередсовпадением$^R-результатвычисленияутверждениявтелешаблона$n-n-ныйфрагментсовпадения\n-n-ныйфрагментсовпадениявызываемыйвсамомшаблоне$+-фрагментсовпадения$*-разрешаетвыполнятьпоисквмногострочныхфайлах@--спецмассив,которыйсодержитначальнуюпозициюнайденногослова@+-массив,содержащийпозицюпоследнегонайденногослова

$&-совпадениесшаблономпоиска,припоследнейоперациипоискаилизамены.Вотличииотпеременнойпеременнуюпереопределятькаквздумаетсянельзя.

$'подстроказасовпадениемсшаблономпоиска,етакжеможнотолькочитать.

$`-подстрока,расположеннаяпередсовпадением,разрешаетсятолькоечтение.

$^R-результатвычисленияутверждениявтелешаблонадляпоследнеговычисленияшаблона,есливнемидетсчетиливызываетсявнешняяпрограмма:

$qwer="lala";

$qwer=~/x(?{$var=5})/;

print$^R;

5

$+-фрагментсовпадениявшаблоне,которыйвнембылпоследнимвкруглыхскобках.Разрешаетсятолькочтение$+.

$* - разрешает выполнятьпоиск вмногострочныхфайлах, булевапеременная, если она взведена вшаблонапоиска^ и$ сопоставляются позициямперед и после внутренних символов новой строки, еслиначалатекстаидоконцатекста:

$kim="lala\nfa\eti\nzvuki...";

$kim=~~/^eti/;#совпадениененашлось

$*=1;

$kim=~~/^eti/;#совпадениенашлось

$n-n-ныйфрагментсовпадения:

Page 17: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

print"$1$2$3\n"if(/^(\d)(\w)(\W)$/);

\n-n-ныйфрагментсовпадениявызываемыйвсамомшаблоне,напримерпоискгиперссылок:

/ahref=(['"])(.*?)\1>/

Напримернужнозанестивмассивтолькоцифрыизстрочки"12@#34@@#@@###34@@##67##@@#@#@34"

$_='12@#34@@#@@###34@@##67##@@#@#@34';

s/@/#/g;

s/(#)\1+/$1/g;

printjoin/\n/,split/#/,$_;

Регулярное выражение s/(#)\1+/$1/g; изпользует повторение переменнойзаменяетвсеподрядидущие#междуцифраминаодну#,содержащуюсявшаблонаилишаблонуказатьвкруглыхскобках).

Допустимнужноопределить,вселицифрычисларазличны.Попробуемнайтихотя-быодноповторяющеесячисло:

if(/(\d).*(?=\1)/g){

print"покрайнеймереоднацифра$1различна\n";

}

Выражение берет 1-ю цифру и ищет е совпадения со всеми остальными, если есть, то говорит, что найдено изаканчиваетработу.Регулярноевыражениеберетпервоечислоприпомощи(\d)иначинаетегосравниватьсовсемиостальными числами при помощи .*(?=\1). Если первое число в строке уникально, регулярное выражение начнетсопостовлять второе число со всеми восемьюоставшимисячислами.Еслии второе число в строке уникально, тоберется третье число и сравнивается со всеми остальными.И т.д., если совпадение было найдено, то регулярноевыражениевозвращает trueизаканчиваетсвоюработу,дажеесливстрокеещеестьповторяющиесячисла.Чтобыможнобылопросмотретьвсеповторяющиесячисла,можновоспользоватьсямодификациейпредыдущегокода:

$_='2314152467';

my@a=m/(\d)(?=\d*\1)/g;

if(@a){

printjoin(',',@a),"-Repeat\n";

}

else{

print"Ok\n";

}

Этотусовершенствованныйкодработаетдотехпор,поканебудутнайденывсесовпадения,еслитаковыевообщеесть.

В perl 5.6 вводятся переменные @- и @+, комбинация которых может заменять переменные

Page 18: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

совпаденияшаблонапеременная$-[0]содержитначалосоответсвиятексташаблону,апеременнаяконецсоответсвиятексташаблону.Вначалепоискаобеявляютсянулями.Этозначит,чтоможновычислитьзначения$`,$&,и$':

$do=substr($stroka,0,$-[0]);

$sovpalo=substr($stroka,$-[0],$+[0]-$-[0]);

$posle=substr($stroka,$+[0]);

Например:

$test="11-231234";

$test=~/\d{2}-\d{6}/;

print"$-[0],$+[0]";

0,9

Соответственноепеременные$#-и$#-указываютразмерностьмассивов@-

Переменная$^N.

Какработаютрегулярныевыражения

Регулярные выражения, использующие квантификаторы,могут порождать процесс, которыйназывается перебор свозвратом (backtracking). Чтобы произошло совпадение текста с шаблоном, надо построить соответствие междутекстомивсемрегулярнымвыражением,анеегочастью.Началошаблонаможетсодержатьквантификатор,которыйпоначалу срабатывает, но впоследствии приводит к тому, что для частишаблона не хватает текста или возникаетнесоответствие между текстом и шаблоном. В таких случаях perl возвращается назад и начинает построениесоответствиямеждутекстомишаблономссамогоначала,ограничивая"жадность"квантификатора(именнопоэтомупроцессиназывается"переборсвозвратом").Перечислимквантификаторыperl:

*-нольилинесколькосовпадений,+-одноилинесколькосовпадений,?-нольсовпаденийилиодносовпадение,{n}-ровноnсовпадений,{n,}-покрайнеймереnсовпадений,{n,m}-отnдоmсовпадений.

Например квантификатор + соответствует фразе "один или несколько" и является жадным. Расмотрим пошаговопринциппереборасвозвратомнапримереквантификатора+:

'aaabc'=~/a+abc/;

Page 19: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

a+сразувсилужадностисовпадаетстремяа:

(aaa)bc

но после aaa не следует строка "abc", а следует "bc". Поэтому результат - failed поэтому анализатор долженоткатитьсяназадивернутьспомощьюa+дваa:(aa)abcт.е.навторомшагешаблоннайдетсовпадение.

Рассмотримпримерработыещеодногожадногоквантификатора*(нольилинесколькосовпадений):

amxdemxg/.*m/

Сначалабудетнайденавсястрокаabcdebfgвсилужадности.*,потомквантификаторунужнобудетнайтисравнениесбуквойm,произойдетошибка.Квантификатор.*отдастоднубуквуиегосодержимоебудетужеснованетбуквыm.Будетотданаещеоднабукваиснованебудетнайденосовпадениесовсемшаблономинаконецквантификатор.*будетсодержатьподстрокуamxde,закоторойужестоитсимволсмотрянато,чтовстрокеamxdemxgсодержитсянеоднабукваm.Потомуиговорят,чтоквантификаторыобладаютжадностью,т.е.находятмаксимальновозможноесовпадение.

Допустимнужнонайтисовпадение:

$uu="Howareyou?Thanks!I'mfine,youareok??";

$uu=~s/.*you//;

print$uu;

Квантификатор.*оставиттекст"areok??",авовсене"?Thanks!I'mfine,youareok??"ограничитель?,которыйвместесознакомквантификатораозначаетмаксимальновозможноесовпадение

$uu="Howareyou?Thanks!I'mfine,youareok??";

$uu=~s/.*you//;

print$uu;

топеременная$uuбудетсодержатьтекст"?Thanks!I'mfine,youareok??"

Предположимнужнонайтисовпадениятипаnetworkworkshop,т.е.перекрытия.

$u='network';

$m='workshop';

print"перекрытие$2найдено:$1$2$3\n"if("$u$m"=~/^(\w+)(\w+)\2(\w+)$/);

$1сразуберетвсесловов$u,нодальшеидетещеодинмаксимальныйквантификаторнадоионзабираетизпеременной\1буквуk(причемтолькоодну):

Page 20: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

#!/usr/bin/perl

$uu="asdfgasdf";

$uu=/(\w+)(\w+)\s(\w+)(\w+)/;

print"$1$2##$3$4";

asdfg##asdf

далеепошаговаяработаregexвыглядитпримернотак:

1:'networ''k'=>'\sk'совпадаетлис'\sworkshop'falure

2:'netwo''rk'=>'\srk'совпадаетлис'\sworkshop'falure

3:'netw''ork'=>'\sork'совпадаетлис'\sworkshop'falure

4:'net''work'=>'\swork'совпадаетлис'\sworkshop'ok

иврезультатепрограммавыдаст:

перекрытиеworkнайдено:networkshop

Данныйрегекспнесработает,если

$u='networkwork';

$m='workshop';

шаблоннайдетперекрытияworkwork,анеwork.Чтобыэтогоизбежать,нужносделатьминимальным(\w+)\2(\w+)$/

Квантификатор действует только на предшествующий ему элементшаблона. Например, конструкциябудетсоответствоватьпоследовательностиизоднойилинесколькихстрочныхлатинскихбукв,начинающейсясдвухцифр,анепоследовательности,составленнойизчередующихсяцифрибукв.Длявыделениягруппыэлементов,накоторуюдействуетквантификатор,используютсякруглыескобки:(\d{2}(a-z])+

Логическиеоперацииврегулярныхвыражениях

В регулярных выражениях perl есть синтаксические выражение, позволяющие в шаблонах использовать простыелогическиеконструкции:

(?= шаблон) - после этой точки есть фрагмент текста, который соответствует указанному регулярномувыражению(?!шаблон)-послеэтойточкинеттекста,которыйбысоответствовалуказанномурегулярномувыражению,(?<=шаблон)-передэтойточкойестьфрагменттекста,соответствующийуказанномурегулярномувыражению,(?<! шаблон) - перед этой точкой нет фрагмента текста, соответствующего указанному регулярномувыражению.

Page 21: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

(?#текст)-комментарий.Тексткомментарияигнорируется.(?:шаблон) или (?модификаторы:шаблон) - группирует элементы шаблона. В отличие от обычных круглыхскобок, не создает нумерованной переменной. Например, модификаторстрочными и заглавными буквами, однако область действия этого модификатора будет ограничена толькоуказаннымшаблоном.(?=шаблон) - "заглядывание вперед". Требует, чтобы после текущей точки находился текст, соответствующийданному шаблону. Такая, конструкция обрабатывается как условие или мнимый символ, поскольку невключаетсяВрезультатпоиска.Например,поиск спомощьюкомандыследуютодинилинесколько"пробельныхсимволов",однакосамиониврезультатневойдут.(?!шаблон) - случай, противоположный предыдущему. После текущей точки не должно быть текста,соотносимогосзаданнымшаблоном.Так,еслишаблонw+(?=\s)-этослово,закоторымследует"пробельныйсимвол",тошаблонw+(?!\s)-этослово,закоторыммет"пробельногосимвола".(?<=шаблон)-заглядываниеназад.Требует,чтобыпередтекущейточкойнаходилсясоответствующийтекст.Так,шаблон (?<=\s)w+ интерпретируется как слово, перед которым имеется пробельный символ (в отличие отзаглядывания вперед, заглядывание назад может работать только с фиксированным числом проверяемыхсимволов).(?<!шаблон)-отрицаниепредыдущегоусловия.Передтекущейточкойнедолжнобытьтекста,соотносимогосзаданным шаблоном. Соответственно, от команды /(?<!\s)w+/ требуется найти слово, перед которым нетпробельногосимвола.(?{код}) - условие (мнимый символ), которое всегда выполняется. Сводится к выполнению команд perl вфигурныхскобках.Выможетеиспользоватьэтуконструкцию,толькоесливначалесценарияуказанакомандаusere'eval'.Припоследовательномсоотнесениитекстаишаблона,когдаperlдоходитдотакойконструкции,выполняется указанный код. Если полного соответствия для оставшихся элементов найти не удалось, то привозврате левее данной точки шаблона вычисления, проделанные с локальными переменными, откатываютсяназад. (Условие является экспериментальным. В документации, прилагаемой в perl, можно найти довольнодетальное рассмотрение (с примерами) работы этого условия и возможных трудностей в случае егоприменения.)(?>шаблон) - "независимый" или "автономный" шаблон. Используется для оптимизации процесса поиска,посколькузапрещает"поисксвозвратом".Такаяконструкциясоответствуетподстроке,накоторуюналагаетсязаданныйшаблон,еслиегозакрепитьвтекущейточкебезучетапоследующихэлементовшаблона.Например,шаблон(?>а*)аbвотличиеотa*abнеможетсоответствоватьникакойстроке.Еслипоставитьвлюбомместешаблона*,онсъествсебуквыа,неоставивниоднойшаблонуab.(Дляшаблона* будет ограничен за счет работы поиска с возвратами: после того как на первом этапе не удастся найтисоответствиемеждушаблономитекстом,perlсделаетшагназадиуменьшитколичествобуквконструкциейа*.)(?(условие)шаблон-да|шаблон-нет)или(?(условие)шаблон-да) -условныйоператор,которыйподставляеттотилиинойшаблонвзависимостиотвыполнениязаданногоусловия.Болееподробноописанвдокументацииperl.(?модификаторы) - задает модификаторы, которые локальным образом меняют работу процедуры поиска. Вотличиеотглобальныхмодификаторов,имеютсилутолькодлятекущегоблока,тоестьдляближайшейгруппы

Page 22: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

круглых скобок, охватывающих конструкцию, Например, шаблон ((?i)text)учетарегистра.

Поискповторяющихсясловврегулярномвыраженииосуществляетсяприпомощит.н.обратныхссылок.Вышеужебылприведенпримерихиспользованиядлявыбираниявсехадресоврисунковсwww.astronomynow.com:

m{SRC\s*=\s*(["'])http://(.*?)\1\s+(.*?)WIDTH="100"HEIGHT="100"(.*?)>}igs

(["'])-найтилибо"либо'либоничего,т.к.src=http://можетбытьбезкавычек.Кактолькобылнаденочто-либоиз этих трех позиций, через минимальное количество символов(регулярное выражениезаносится в специальную переменную \1, которая вне m/.../ может быть вызвана каквызываетсявеголевуюполовинукак$1).Дальшепосле*.gif|*.jpg|*.bmpодин пробел \s+, т.к. броузеры воспримут подстроку src=file.gifborder=0gifborder=0.Поэтомуданноерегулярноевыражениевполнеисправноработает,хотяонобылосделанодлясайта,гдевimgsrc ставится полный адрес, т.е. начинающийся сhttp:// Для других сайтов придется выстраивать полныепутивссылкахиспользуяbasehref,еслиестьилиегоurl.Еслинужнонайтикакое-топосчетусовпадениешаблонавстроке,тоэтореализуетсяпримернотак:

while($str=~/WHAT/g){$n++}

$n++while$str=~/WHAT/g;

$n++while$str=~/(?=WHAT)/g;#дляперекрывающихсясовпадений

for($n=0;$n=~/WHAT/g;$n++){}

Каждоекратноесовпадение

(++$n%6)==0;

НужноеВамсовпадение:

$n=($str=~/WHAT/gi)[6];#допустимшестое

Иликаждоечетноесовпадение

@mass=grep{$n++%2==0}/WHAT/gi;

длянечетногонужнонаписатьвнутриgrep:$n++%2==1Логическиеоперациивнутрирегулярныхвыражений.Еслинужнонайтипоследнеесовпадение,томожновоспользоватьсяотрицаниемопережающейпроверки

m#PATTERN(?!.*PATTERN)$#

т.е. нийти какой-то PATTERN, при этом не должно найтись что-то еще(.*совпадение;

Минимальныеквантификаторы*?,+?,??,{}?

Page 23: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

допустимнужнонайтидвойку,передкоторойнестоит3илипробел:

print"$1\n"whilem%2(?![3\s])gm%;

используетсяусловиепоотрицанию,A(?!B):найтиА,передкоторымненаходитсястоит3илипробле(\s),томожновоспользоваться:

print"$1\n"whilem%2(?=[3\s])gm%;

или

print"$1\n"whilem%2(?![^3\s])gm%;

гдеиспользуется^,[^3\s],которыйзначитследущее:вкласссимволов,которыенужнонайти,невходятилидругимисловаминайтивсекроме3и\s.

ДопустимсуществуетHTML-документ,вкоторомпроизвольноечисловложенныхтаблиц[&Требуется "вырезать" по очереди самые вложенные таблицы (не содержащие внутри [соответственно, выводить. И так - рекурсивно до конца вырезать изнутри всю таблицу. Ниже представленапрограмма,реализующаяэтузадачуприпомощилогическогооператора(?!...)

#!/usr/bin/perl-wT

$file=qq|s<table>aaabbb

<table>cc<table>ccc

<table>2<table>bb</table><table>cc</table></table></table>cc

</table>

ddd</table>d

|;

print$file;

&req($file);

subreq{

if($file=~m%(<table>((?!.*<table>).*?)</table>)%igs){

$file=~s%(<table>((?!.*<table>).*?)</table>)%%igs;

print"Virezali--$1--";

&req($file);

}

return$file;

}

ПродолжаемрассматриватьлогическиеоператорыврегулярныхвыраженияхнаопретаорахтипаOR,ANDилиNOT.

Регекспистиннен,если/AM|BMA/или/AM/||/BMA/иеслиестьперекрытиетипа

Page 24: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

/^(?=.*AM)(?=.*BMA)/s

Выражениеистинноесли/AM/и/BMA/совпадаютприперекрытиикотороенеразрешено:

/AM.*BMA|BMA.*AM/s

Выражениеистинно,еслишаблон/ABC/несовпадает:

!~/ABC/

или

/^(?:(?!ABC).)*$/s

Выражениеистинно,еслиABCнесовпадает,аVBNсовпадает:

/(?=^(?:(?!ABC).)*$)VBN/s

Несовпадениеможнопроверитьнесколькимиспособами:

unless($str=~/MMM/){...}

if(!($str=~/MMM/)){...}

if($str!~/MMM/){...}

Дляобязательногосовпадениявдвухшаблонах:

unless($str!~/MMM/&&$str!~/BBB/){...}

#или

if($str=~/MMM/&&$str=~/BBB/){...}

Хотябыводном

unless($str!~/MMM/||$str!~/BBB/){...}

#или

if($str=~/MMM/||$str=~/BBB/){...}

Регулярныевыражения -основаработысоператорамиm/.../ иs/.../.../качестве аргументов. Разберемся, как устроено регулярное выражениеотдельныхсловвстроке:

$text="Perlisthesubject.";

$text=~/\b([A-Za-z]+)\b/;

print$1;

Выражение \b([A-Za-z]+)\b включает в себя группирующие метасимволыкласс всех латинских букв [A-Za-z] (он объединяет заглавные и строчные буквы) и квантификатор

Page 25: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

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

Совпадениеслюбымсимволом

Вperlимеетсяещеодинмощныйсимвол-аименно,точка(.).Вшаблонеонсоответствуетлюбомузнаку,кромесимвола новой строки. Например, следующая команда заменяет в строке все символы на звездочки (использованмодификаторg,обеспечивающийглобальнуюзамену):

$text="Nowisthetime.";

$text=~s/./*/g;

print$text;

********************

А что делать, если требуется проверить совпадение именно с точкой? Символы вроде точки (конкретно,[{^$*+?.), играющие в регулярном выражении осббую роль) называются, как уже было сказано выше,метасимволами, и если вы хотите, чтобы они внутри шаблона интерпретировались как обычные символы,метасимволу должна предшествовать обратная косая черта. Точно так же обратная косая черта предшествуетсимволу, используемому в качестве ограничителя для команды m/.../встречаетсявнутришаблонаинедолженрассматриватьсякакограничитель.Рассмотримпример:

$line=".Hello!";

if($1ine=-m/\./){

print"Shouldn'tstartasentencewithaperlod!\n";

}

Shouldn'tstartasentencewithaperlod!

Еслинужнонайтисамыйкороткийтекстовыйфрагмент/QQ(.*?)FF/в"QQffQQffFF"ff".Шаблонвсегданаходитлевуюстрокуминимальнойдлины,котораясоответствуетвсемушаблону, т.е. этовсястрокавэтомпримере.Дляправильногошаблонанужновоспользоватьсялогическимиоператорамиврегулярныхвыражениях:/QQ((?:(?!QQ).)*)FF/,т.е.сначалаQQ,потомнеQQ,потомFF

Конструкции(?<=шaблoн)и(?<!шаблон)работаюттолькосшаблонами,соответствующимификсированномучислусимволов.Инымисловами,вшаблонах,указываемыхдля(?<=...)и(?<!...)

Этиусловияполезны,еслинужнопроверить,чтопередопределеннымфрагментомтекстаилипосленегонаходитсянужная строка, однако ее не требуется включать в результат поиска. Это бывает необходимо, если в кодеиспользуются спе-циальные переменные $& (фрагмент, для которого найдено соответствие между текстом ирегулярнымвыражением),$`(текст,предшествующийнайденномуфрагменту)ифрагментом).Болеегибкимпредставляетсяприменениенумерованныхпеременных

Page 26: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

отдельныечастинайденногофрагмента.

Вследующемпримереищетсяслово,закоторымследуетпробел,носампробелневключаетсяврезультатпоиска:

$text="МаrуTomFrank";

while($text=~/\w+(?=\s)/g){print$&."\n";}

Маrу

Tom

Frank

Тогожерезультатаможнодобиться,еслизаключитьвкруглыескобкиинтересу-ющуюнасчастьшаблонаизатемиспользоватьеекакпеременную$1:

$text="MaryTomFrank";

while($text=~/(\w+)\s/g){

print$1."\n";

}

Маrу

Tom

Frank

Следует четко понимать, что вы имеете в виду, когда используете то или иное условие. Рассмотрим следующийпример:

$text="Mary+Tom";

if($text=~m|(?!Mary\+)Tom|){

print"TomiswithoutMary!\n";

}

else{

print"Tomisbusy...\n";

}

Вопрекинашиможиданиям,perlнапечатает:TomiswithoutMary!Этопроизойдетпоследующейпричине.Пробуяразличныеначальныеточкивходнойстроки,откоторойначинаетсясопоставлениешаблонаитекста,pеr1раноилипозднодоберетсядопозиции,расположеннойпрямопередименем"Tom".Условиетекущейточкиненаходилсятекст*Маry+",иэтоусловиедлярассматриваемойточкибудетвыполнено.Далее,perlпоследовательнопроверяет,чтопослетекущейточкиследуютбуквы"Т","o"и"m",иэтотребованиетакжевсиле(после проверки условия (?!Маry\+) текущая точка остается на месте). Тем самым найдено соответствие междуподстрокой"Тоm"ишаблоном,поэтомукомандапоискавозвращаетзначениеистина.

Регулярное выражение (?!Mary\+)....Tom, резервирующее четыре символа под текст "выше случая выведет то, что требовалось, но выдаст ошибочный ответ, если перед именем "Тоm" нет четырех

Page 27: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

символов:

$text="O,Tom!";

if($text=~m|(?!Mary\+)....Tom|){

print"TomiswithoutMary!\n";

}

else{

print"Tomisbusy...\n";

}

Tomisbusy...

Наконец,еслиболееточносформулировать,чеготребуется,получитсянужныйрезультат:

$text="Mary+Tom";

if($text=~m|(?<!Mary\+)Tom|){

print"TomiswithoutMary!\n";

}

else{

print"Tomisbusy...\n";

}

Tomisbusy...

Вспомнитьинаписатьпрострочкувида

push@mass,$liunless($li=~m/(([2..12]).*?1995)|(([6..12]).*?2001)|/)

;perldocperlop[0-9.]

Модификаторыкомандm/.../иs/.../.../

Вperlимеетсянесколькомодификаторов,используемыхскомандамиm/.../

i-игнорируетразличиемеждузаглавнымиистрочнымибуквами.s-метасимволу"точка"разрешеносоответствоватьсимволам\n.m - разрешает метасимволам ^ и $ привязываться к промежуточным символамвлияетнаработуметасимволов\А,\Zи\z.х-игнорирует"пробельныесимволы"вшаблоне(имеютсяввиду"истинные"пробелы,анеметасимволыпробелы,созданныечерезescape-последовательности).Разрешаетиспользоватьвнутришаблонакомментарии.g-выполняетглобальныйпоискиглобальнуюзамену.с-послетогокаквскалярномконтекстеприпоискесмодификаторомне позволяет сбрасывать текущую позицию поиска. Работает только для командымодификаторомg.о - запрещает повторную компиляцию шаблона при каждом обращении к данному оператору поиска или

Page 28: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

замены, пользователь, однако, должен гарантировать, что шаблон не меняется между вызовами данногофрагментакода.е-показывает,чтоправыйаргументкомандыs/.../.../-этофрагментывыполняемогокода.Вкачестветекстадляподстановкибудетиспользовановозвращаемоезначение-возможно,послепроцессаинтерполяции.ee-показывает,чтоправыйаргументкомандыs/.../.../-этостроковоевыражение,котороенадовычислитьивыполнить как фрагмент кода (через функцию eval). В качестве текста для подстановки используетсявозвращаемоезначение-возможно,послепроцессаинтерполяции

Особенностиработыкомандm/.../иs/.../.../

До сих пор мы рассматривали регулярные выражения, используемые в качестве шаблонов для командs/.../.../,инеособоинтересовались,какработаютэтикоманды.Насталовремявосполнитьпробелы.

Командаm/.../ ищет текст по заданномушаблону.Ее работа и возвращаемое значение сильно зависят от того, вскалярномилисписковомконтекстеонаиспользуетсяиимеетсялимодификатор

Командаs/.../.../ищетпрототип,соответствующийшаблону,и,еслипоискоказываетсяуспешным,заменяетегона новый текст. Без модификатора замена производится только для первого найденного совпадения, смодификатором g выполняются замены для всех, совпадений во входном тексте. Команда возвращает в качестверезультатачислоуспешныхзаменилипустуюстроку(условиеложьfalse),еслиниоднойзаменысделанонебыло.Вкачествеанализируемоготекстаиспользуется$_(режимпоумолчанию)иливыражение,присоединенноекшаблонуспомощьюоператора=~или!~.Вслучаепоиска(командаm/.../)конструкция,расположеннаяслеваотоператоров=~ или !~, может и не быть переменной. В случае замены (команда s/.../.../скалярнаяпеременная,илиэлементмассива,илиэлементхэша,илижекомандаприсвоенияодномуизуказанныхобъектов.

Вместо косой черты в качестве ограничителя для аргументов команд m/.../любой символ, за исключением "пробельного символа", буквы или цифры. Например, в этом качестве можноиспользоватьсимволкомментария,которыйбудетработатькакограничитель:

$text="ABC-abc";

$text=~s#B#xxx#ig;

print$text;

AxxxC-axxxc

Вкачествеограничителейнестоитиспользоватьвопросительныйзнакиапостроф(одинарнуюкавычку)-шаблоны,с такими ограничителями обрабатываются специалиным образом. Если командачертывкачестверазделителя,тобуквуmможноопустить:

Page 29: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

while(defined($text=<>)){if($text=~/^exit$/i){exit;}}

Если в качестве ограничителя для команды m/.../ используется вопросительный знак, то букву m также можноопустить.Однакошаблоны,ограниченныесимволом?, в случаепоискаработаютособымобразом (независимоотналичия или отсутствия начальной m). А именно, они ведут себя как триггеры, которые срабатывают один раз ипотом выдают состояние ложь (false), пока их не взведут снова, вызвав функцию reset (она очищает статусблокировки сразу всех конструкций ?...?, локальных для данного пакета). Например, следующий фрагментсценарияпроверяет,естьливфайлепустыестроки:

while(<>)

if(?^$?){print."Thereisanemptylinenere.\n";}continue{

resetifeof;#очиститьдляследующегофайла

}

Диагностическоесообщениебудетнапечатанотолькоодинраз,дажеесливфайлеприсутствуетнесколькопустыхстрок.Командапоискасвопросительнымзнакомотноситсякподозрительнымкомандам,апотомуможетневойтивновыеверсииperl.1Вкачествеограничителейможнотакжеиспользоватьразличные(парные)койструкциискобок:

while(<>){

if(m/^quit$/i){exit;}

if(m(^stop$)i){exit;}

if(m[^end$]i){exit;}

if(m{^bye$}i){exit;}

if(!1)<^ехit$>i){exit;}

}

В случае команды s/.../.../ и использования скобок как ограничителей для первого аргумента, ограничителивторогоаргументамогутвыбиратьсянезависимо:

$text=~"Perliswonderful";

$text=~s/is/isvery/;

$text=~s[wonderful]{beautiful};

$text=~s(\.)/!/;

print$text;

Perlisverybeautiful!

Предварительнаяобработкарегулярныхвыражений

Аргументами команд m/.../ и s/.../.../ являются регулярные выражения, которые перед началом работыинтерполируютсяподобнострокам,заключеннымвдвойныекавычкиВотличиеоттекстовыхстрок,дляшаблонаневыполняется интерполяция имен типа$),$| и одиночного $ - perl считает, что такие конструкции соответствуютметасимволу конца строки, а не специальной переменной. Если же в результате интерполяции шаблон поискаоказалсяпустойстрокой,perlиспользуетпоследнийшаблон,которыйприменялсяимдляпоискаилизамены.

Page 30: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Если вы не хотите, чтобы perl выполнял интерполяцию регулярного выражения, в качестве ограничителя надоиспользоватьапостроф (одиночнуюкавычку), тогдашаблонбудетвестисебя,кактекстоваястрока, заключеннаявапострофы. Однако, например, в случае команды замены s/.../.../ с модификатором е или ее (их работаописывается чуть дальше) для второго аргумента будет выполняться интерполяция даже в том случае, если онзаключенвапострофы.

Есливыуверены,чтоприлюбомобращенииккомандепоискаилизаменышаблоностаетсянеизменным(например,несмотря на интерполяцию, скалярные переменные внутришаблона не будут менять своего значения), то можнозадатьмодификаторо.Тогдаperlкомпилируетшаблонвсвоевнутреннеепредставлениетолькоприпервойвстречесданнойкомандойпоискаилизамены.Приостальныхобращенияхккомандебудетиспользоватьоткомпилированноезначение.Однако, если внезапно изменить значение переменных, задействованных вшаблоне, perl этого даже незаметит.

Командазаменыs/.../.../используетрегулярноевыражение,указанноевкачествевторогоаргумента,длязаменытекста.Посколькуонообрабатывается(интерполируется)послетого,каквыполненаочереднаяоперацияпоиска,внемможно,вчастности,использоватьвременныепеременные,созданныенаэтапепоиска.Вследующемпримеремы последовательно заменим местами пары слов, заданных во входном тексте, оставив между ними по одномупробелу:

$text="OneTwoThreeFourFiveSix";

$text=-s/(\w+)\s*(\w+)/$2$1/g;

TwoOneFourThreeSixFive

Однако perl допускает и более сложные способы определения заменяющего текста. Так, если для командыs/.../.../ указать модификатор е, то в качестве второго аргумента надо указать код, который необходимовыполнить (например, вызвать функцию). Полученное выражение будет использованокак текст для подстановки.Приэтомпослевычислениятекстовогозначения,нопердегоподстановкойбудетвыполненпроцессинтерполяции,аналогичныйпроцессуинтерполяциитекстовыхстрок,заключенныхвдвойныекавычки.Ещеболеесложнаясхемареализуется, если задан модификатор ее. В этом слу-чае второй аргумент командывыражение, которое сперва надо вычислить (то есть интерполировать), затем выполнить в качестве кода (вызваввстроенную функцию eval) и только после второй интерполяции полученный результат подставляется вместонайденноготекста.

Работакомандыm/.../врежимеоднократногопоискаВскалярномконтекстеибезмодификаторавозвращаетлогическоезначение-целоечисло1(истина(true)),еслипоискоказалсяуспешным,ипустуюстроку""(ложь (false)), еслинужныйфрагмент текстанайтине удалось.Если внутришаблонаимеются группы элементов,заключенныевкруглыескобки,топослеоперациипоискасоздаютсянумерованныепеременныесодержитсятекст,соответствующийкруглымскобкам.Вчастности,есливесьшаблонзаключитьвкруглыескобки,тов случаеуспешногопоискапеременная$1 будет содержать текст, соотнесенныйсшаблоном.Послеуспешногопоискаможнотакжеиспользоватьспециальныепеременные$&,$',$'и$+

Page 31: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

$text="---one---two---three---";

$scalar=($text='m/(\w+)/);

print"Result:$scalar($1).";

Result:1(one).

Есливыиспользуетекомандуm/.../всписковомконтексте,товозвращаемоезначениесильнозависитоттого,естьлигруппыизкруглыхскобокввашемшаблоне.Еслиониесть(тоестьеслисоздаютсянумерованныепеременные),топослеуспешногопоискавкачестверезультатабудетполученсписок,составленныйизнумерованныхпеременных($1,$2,...):

$text="---one,two,three---";

array=($text='m/(\w+),\s+(\w+),\s+(\w+)/);

printjoin"=",array;

one=two=three.

В отличие от ранних версий, perl 5 присваивает значения нумерованным переменным, даже если команда поискаработаетвсписковомконтексте:

$text="---one,two,three---";

($Fa,$Fb,$Fc)=($text=-m/(\w+),\s+(\w+),\s+(\w+)/);

print"/$Fa/$Fb/$Fc/\n";

print"$1=$2=$3.\n";

/one/two/three/

one=two::three.

Еслижевшаблоненетгрупп,выделенныхкруглымискобками,товслучаеуспешногопоискавозвращаетсясписок,состоящийиз одного элемента - числа1.При неудачномпоиске независимо от того, были ли вшаблоне круглыескобки,возвращаетсяпустойсписок:

$text="---one,two,three---";

@array=($text=~m/z\w+/);

print"Result:/",@array,"/\n";

print"Size:",$#array+1,".\n";

Result://

Size:0.

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

Работакомандыm/.../врежимеглобальногопоиска

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

Page 32: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

$text="---one---two-~-three---";

@array=($text=~m/(-(\w+))/);

print"Single:[",join(",",array),"].\n";

@array=($text=~m/(-(\w+))/g);

print"Global:[",join(",",array),"].\n";

Single:[-one,one].

Global:[-one,one,-two,two,-three,three].

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

$text="---one---two---three--";

@array=($text=~m/\w+/);

print"Result:(",join(",",@array),").\n";

Result:(one,two,three).

Вслучаенеудачногопоиска,какивпредыдущихвариантах,возвращаетсяпустойсписок.Вскалярномконтекстеисмодификатором g комaндa m/.../ ведет себя сивершенно особым образом. Специальная переменнаяпеременная, стоящая слева от оператора =~ или !~, при поиске с модификаторомсвойства - в нее записывается последнее состояние.При каждом последующем обращении к данномуфрагментукода поиск будет продолжаться с того места, на котором он остановился в последний раз. Например, следующаякомандаподсчитываетколичествобуквхвзаданнойстрокетекста:

$text="Hereistexxxxxt.";

$counter=O;

while($text=~m/x/g){

print"Foundanotherx.\n";

$conter++;

print"Totalamount=$counter.\n";

Foundanotherх.Foundanotherх.

Foundanotherx.

Foundanotherx.Foundanotherx.

Totalamount=5.

Состoяние (точнее, позиция) поиска сохраняется даже в случае перехода к следующему оператору поиска,имеющемумодификаторg.Неудачныйпоисксбрасываетзначениевисходноесостояние,еслитолькодлякомандыm/.../неуказанмодификаторс (тоестькомандадолжнаиметьвидm/.../которого выполняется поиск, также сбрасывает позицию поиска в исходное состояние. В следующем примере изтекстовой строки последовательно извлекаются и выводятся пары имя/значение до тех пор, пока строка незакончится:

$text="X=5;z117e=3.14l6;temp=lQ24;";

Page 33: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

$docycle=1;$counter=0;

while($docycle){undef$name;undef$value;

if($text=~m/(\w+)\s*=\s*/g){$name=$1;}if($text=~m/([\d\.\*\-]*)\s*;/g){$value=$1;}

if(defined($name)anddefined($value)){print"Name=$name,Value=$value.\n";

$counter++,

}else{

$docycle=0;

}

}

print"Ihavefound$contervalues.\n";

Name=X,Value=5.

Name=z117e,Value=3.1416.Name=temp,Value=1024.

Ihavefound3values.

Позиция, на которой остановился поиск, может быть прочитана и даже переустановлена с помощью встроеннойфункции perl pos. В шаблоне на текущую позицию поиска можно ссылаться с помощью метасимволаследующемпримереизстрокипоследовательноизвлекаютсябуквыp,oиq

$index=0;

$_="ppooqppqq";

while($index++<2){

print"1:'";

print$1while/(o)/gc;print"',pos=",pos,"\n";

print"2:'";

print$1if/\G(q)/gc;print"',pos=";'pos,"\n";

print"3:'";

printwhile/(p)/gc;print"',pos=",pos,"\n";

}

1:'oo',pos=4;

2:'q',pos=7;

3:'pp',pos=4;

1:'',pos=7;

2:'q',pos=8;

3:'',pos=8;

В документации perl приводится основанный на этом механизме интересный пример последовательноголексическогоразборатекста.Внемкаждаяпоследующаякомандапоискаочереднойлексическойединицыначинаетвыполнятьсяс того места, где завершила свою работу предыдущая. Советую внимательно разобраться с этимпримером (страница руководства perlop, раздел "Regexp Quote-Uke Operatorsесливыхотитерасширитьдоступныйваминструментарийperl!

Заменастрокспомощьюкомандыtr/.../.../

Кромекомандm/.../иs/.../.../строкиможнообрабатыватьспомощьюкомандыу/.../.../):

Page 34: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

tr/список1/список2/модификаторы;

у/список1/список2/модификаторы;

В отличие от m/.../ и s/.../.../, эта команда не использует шаблоны и регулярные выражения, а выполняетпосимвольнуюзамену,подставляявтекствместолитеризпервогоспискасоответствующиеимлитерыизвторогосписка.Например,вследующемслучаепроизводитсязаменалитер"i"на"=~tr/i/o/;print$text;MynameisTom.

В качестве списков используются идущие друг за другом символы, не разделяемые запятыми (то есть это скореестроки, чем списки). В отличие от шаблонов команд m/.../ и s/.../.../интерполируются (то есть подстановки значений вместо имен переменных не происходит), хотя escape-последовательности, указанные внутри аргументов, обрабатываются правильно. Подобнокомандаtr/.../.../пoумолчаниюработаетспеременной$_:

while(<>){

tr/iI/jJ/;

print;

В качестве списков можно указывать диапазоны символов - как, например в следующем фрагменте кода,заменяющемстрочныебуквыназаглавные:$text="Hereisthetext.";$text=~tr/a-z/A-Z/;print$text;HEREISTHETEXT.

Как и в случае m/.../ u s/.../.../, команда tr/.../.../ не требует использовать именно знаки косой черты вкачествеограничителей.Можноиспользоватьпрактическилюбойсимвол,отличныйот"пробельных",буквицифр,атакжепарныескобочныеконструкции.

Командаtr/.../.../ возвращает число успешных замен.В частности, если не было сделано никаких замен, онавозвращает число ноль. Это позволяет, например, подсчитать с помощью командывхожденийбуквыхвстроку$text,неменяясодержимогоэтойпеременной:= ($text =~tr/x/x/); print $xcount; 1 Если у команды tr/.../.../"Модификаторыкомандыtr/.../.../"),тоееаргументыприобычныхусловияхдолжныбытьодинаковойдлины.Если второй аргумент длиннее первого, то он усекается до длины первого аргумента - так, командаэквивалентна команде tr/abc/012/. Если первый аргумент длиннее второго и второй не пуст, то для второгоаргумента необходимое число раз повторяется его последний символ - так, командакоманде tr/0123456789/abcccccccc/. Еслиже второй, аргумент пуст, то команданегопервыйаргумент.

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

Page 35: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

$text="Pi=3.1415926536,е=2.7182";

$digit_counter=($text=~tr/0-9//);

print$digit_counter;

16

Командаtr/.../.../работаетбезрекурсии,простопоследовательнозаменяетсимволывходноготекста.Например,длязаменызаглавныхбуквнастрочные,ина-оборот,достаточновыполнитькоманду:

$text="MSWindows95/98/NT";

$text="tr/A-Za-z/a-zA-Z/;

print$text;

msWINDOWS95/98/nt

Есливсписке,указанномвкачествепервогоаргумента,естьповторяющиесясимволы,тодлязаменыиспользуетсяпервоевхождениесимвола:

$text="BillyGates";

$text=~tr/ttt/mvd/;

print$text;

BillyGames

Модификаторыкомандыtr/.../.../

Командаtr/.../.../допускаетиспользованиеследующихмодификаторов:

d-удаляетнепарныесимволы,невыравниваяаргументыподлине.с - в качествепервого аргументаиспользуетполныйсписокиз256 символов за вычетомуказанныхв спискесимволов.s-удаляетобразовавшиесяврезультатезаменыповторяющиесясимволы.

Если указанмодификатор d, a первый аргумент команды длиннее второго, то все символы из первого списка, неимеющие соответствия со вторым списком, удаляются из обрабатываемого текста. Пример: удаляем строчныелатинскиебуквыизаменяемпробелынаслэши:

$text="Hereisthetext.";

$text=~tr[a-z][/]d;

print$text;

H///.

Наличие модификатора d - единственный случай, когда первый и второй аргументы не выравниваются друготносительно друга, В остальных вариантах второй аргумент либо усекается, либо последний символ в немповторяетсядотехпор,покааргументынесравняются,либо,есливторойаргументпуст,вместоВторогоаргумента

Page 36: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

беретсякопияпервого.

Если указан модификатор с, то в качестве первого аргумента рассматриваются все символы, кроме указанных.Например,заменимназвездочкивсесимволы,кроместрочныхлатинскихбукв:

$text="Hereisthetext,";

$text='tr/a-z/*/c;

print$text;

*ere*is*the*text*

Еслиуказанмодификаторs,товслучаееслизамещаемыесимволыобразуютцепочкиизодинаковыхсимволов,онисокращаются до одного.Например, заменим слова, состоящиеиз латинских букв, на однократные символыкосойчерты:

$text="Hereisthetext.";

$text="tr(A-Za-z)(/)s;

print$text;

////.

Безмодификатораsрезультатбылбыдругим:

$text="Hereisthetext.";

$text='tr(A-Za-z)(/);

print$text;

/////////////.

Примеры:1.Заменитьмножественныепробелыинетекстовыесимволынаодиночныепробелы:

$text="Hereisthetext."

$text=~tr[\000-\040\177\377][\040]s;

print$text;

Hereisthetext.

2.Сократитьудвоенные,утроенныеит.д.буквы;

$text="Hereisthetexxxxxxt.";

$text=~tr/a-zA-Z/s;

print$text;

Hereisthetext.

3.Пересчитатьколичествонебуквенныхсимволов:

$xcount=($text=~tr/A-Za-z//c);

4.Обнулитьвосьмойбитсимволов,удалитьнетекстовыесимволы:

Page 37: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

$text=-tr{\200-\377}{\000-\l77};

$text=~tr[\000-\037\177][]d;

5.Заменитьнетекстовыеи8-битныесимволынаодиночныйпробел:

$text=~tr/\021-\176//cs;

Поискотдельныхслов

Чтобывыделитьслово,можноиспользоватьметасимвол\Sсоответствующийсимволам,отличнымот"пробельных":

$text="Nowisthetime.";

$text=-/(\S+)/;

print$1;

Now

Однако метасимвол \S соответствует также и символам, обычно не используемым для идентификаторов. Чтобыотобратьслова,составленныеизлатинскихбукв,цифрисимволовподчеркивания,нужноиспользоватьметасимвол\w:

$text="Nowisthetime.";

$text=~/(\w+)/;

print$1;

Now

Еслитребуетсявключитьвпоисктольколатинскиебуквы,надоиспользоватькласссимволов:

$text="Nowisthetime.";

$text=~/([A-Za-z]+)/;

print$1;

Now

Болеебезопасныйметодсостоитвтом,чтобывключитьвшаблонмнимыесимволыграницыслова:

$text="Howisthetime.";

$text=~/\b([A-Za-z]+)\b/;

print$1;

Now

Привязкакначалустроки

Началу строки соответствует метасимвол (мнимый символ) ^. Чтобы шаблон к началу строки, надо задать этотсимволвначалерегулярноговыражения.Например,воттакможнопроверить,чтотекстненачинаетсясточки:

$line=".Hello!";

if($line=~m/^\./){

Page 38: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

print"Shouldn'tstartasentencewithaperiod!\n";

}

Shouldn'tstartasentencewithaperiod!

Чтобыточка,указаннаявшаблоне,неинтерпретироваласькакметасимволпереднейпришлосьпоставитьобратнуюкосуючерту.

Привязкакконцустроки

Чтобы привязать шаблон к концу строки, используется метасимвол (мнимый символ)используемпривязкушаблонакначалуикконцустроки,чтобыубедиться,чтопользовательввелтолькослово"exit":

while(<>){

if(m/"exlt$/){exit;}

}

Поискчисел

Для проверки того, действительно ли пользователь ввел число, можно использо-вать метасимволыМетасимвол\Dсоответствуетлюбомусимволу,кромецифр.Например,следующийкодпроверяет,действительноливведенныйтекстпредставляетсобойцелоезначениебеззнакаипаразитныхпробелов:

$test="Hello!";

if($text=~/\D/){

print"Itisnotanumber.\n";

}

Itisnotanumber.

Toжесамоеможнопроделать,использовавметасимвол\d:

$text="333";

if($text=~/^\d+$/){

print"Itisanumber.\n";

}

Itisanumber.

Вы можете потребовать, чтобы число соответствовало привычному формату. То есть число может содержатьдесятичнуюточку,передкоторойстоитпокраййеймереоднацифраи,возможно,какие-тоцифрыпосленее:

$text="3,1415926";

if($text=~/^(\d+\.\d*|\d+)$/){

print"Itisanumber.\n";

}

Itisanumber.

Page 39: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Крометого,припроверкеможноучитывать тотфакт,чтопередчисломможетстоятькакплюс, такиминус (илипустоеместо):

$text="-2.7182";

if($text=~/^([+-]*\d+)(\.\d*|)$/){

print"Itisanumber.\n";

Поскольку плюс является метасимволом, его надо защищать обратной косой чертой. Однако внутри квадратныхскобок,тоестьклассасимволов,оннеможетбытьквантификаторам.Знак"минус"внутриклассасимволовобычноиграетрольоператорадиапазонаипоэтомудолжензащищатьсяобратнойкосойчертой.Однаковначалеиливконцешаблона он никак не может обозначать диапазон, и поэтому обратная косая черта необязательна.Наконец, болеестрогаяпроверка,требует,чтобызнак,еслионприсутствует,былтолькоодин:

$text="+0.142857142857142857";

if($text=~/^(+|-|)\d+(\.\d*\)$/){

print"Itisanumber.\n";

}

Itisanumber.

Альтернативныешаблоны,еслиониприсутствуют,проверяютсяслеванаправо.Переборвариантовобрывается,кактолько найдено соответствие между текстом и шаблоном. Поэтому, например, порядок альтернатив в шаблоне(\.\d*|) мог бы стать критичным, если бы не привязка к концу строки. Наконец, вот как можно произвестипроверкутого,чтотекстявляетсяшестна-дцатеричнымчисломбеззнакаиостальныхатрибутов:

$text="1AO";

unless(ftext=~m/^[a-fA-F\d]+$/){

print"Itisnotahexnumber,\n";

}

Проверкаидентификаторов

Спомощьюметасимвола\wможнопроверить,состоитлитексттолькоизбукв,цифрисимволовподчеркивания(этотесимволы,которыеperlназываетсловесными(wordcharacters)):

$text="abc";

if($text=~/^\w+$/){

print"Onlywordcharactersfound.\n";

}

Onlywordcharactersfound.

Однако, если вы хотите убедиться, что текст содержит латинские буквы и несодержит цифр или символовподчеркивания,придетсяиспользоватьдругойшаблон:

Page 40: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

$text="аbс";

if($text=~/^[A-Za-z]+$/)

{print"Onlylettercharactersfound.\n";}

Qnlylettercharactersfound.

Наконец,дляпроверки,чтотекстявляетсяидентификатором,тоестьначинаетcясбуквыисодержитбуквы,цифрыисимволыподчеркивания,можноиспольpоватькоманду:

$text="X125c";

if($text=~/^[A-Za-z]\w+$/)

{print"Thisisidentifier.\n";}

Thisisidentifier.

Какнайтимножественныесовпадения

Дляпоисканесколькихвхожденийшаблонаможноиспользоватьмодификаторужевиделиранее,используеткомандуm/.../смодификаторомgдляпоискавсехвходженийбуквы

$text="Hereistexxxxxt";

while($text=~m/x/g){

print"Foundanotherx.\n";

}

Foundanotherx.

Foundanotherx.

Foundanotherx.

Foundanotherx.

Foundanotherx.

Модификаторgделаетпоискглобальным.Вданном(скалярном)контекстеperlпомнит,гдеоностановилсявстрокеприпредыдущемпоиске.Следующийпоискпродолжаетсясотложеннойточки.Безмодификаторабудетупорнонаходитьпервоевхождениебуквых,ициклбудетпродолжатьсябесконечно.

Вотличиеот командыm/.../ командаs/.../.../ смодификаторомg выполняет глобальную замену за один раз,работая так, будто внутри нее уже имеется встроенный цикл поиска, подобный приведенному выше.Следующийпримерзаодинраззаменяетвсевхожденияхнаz:

$text="Hereistexxxxxt.";

$text=~s/x/z/g;

print$text;

Hereistezzzzzt.

Безмодификатораgкомандаs/.../.../замениттолькопервуюбуквух.Командазначениячислосделанныхподстановок,чтоможетоказатьсяполезным:

Page 41: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

$text="Hereistexxxxxt.";

print(text=~s/x/z/g)

5

Поискнечувствительныхкрегиструсовпадений

Вы можете использовать модификатор i, чтобы сделать поиск нечувствительным к разнице между заглавными истрочнымибуквами.Вследующемпримерепро-граммаповторяетнаэкраневведенныйпользователемтекстдотехпор,поканебудетвведеноQ,илиq(сокращениедляQUITилиquit),послечегопрограммапрекращаетработу:

while(<>){

chomp;

unless(/^q$/i){

print

}

else{

exit;

}

}

Выделениеподстроки

Чтобыполучитьнайденнуюподстрокутекста,можноиспользоватькруглыескобкивтелешаблона.Еслиэтоболееудобно,можнотакжеиспользоватьвстроеннуюфункциюsubstr.Вследующемпримеремывырезаемизтекстовойстрокинужныйнамтипизделия:

$record="Productnumber:12345

Producttype:printer

Productprice:$325";

if($record=~/Producttype:\s*([a-z]+)/i){

print"Theproduct'stypeIs^$1.\n";

}

product'stypeisprinter.

Вызовфункцийивычислениевыраженийприподстановкетекста

Используя для команды s/.../.../ модификатор е, вы тем самым показываете, что правый операнд (то естьподставляемыйтекст)-этотовыражениеperl,котороенадовычислить.Например,спомощьювстроеннойфункцииperluc(uppercase)можнозаменитьвсестрочныебуквысловстрокиназаглавные:

$text="Nowisthetime.";

$text=~s/(\w+)/uc($1)/ge;

print$text;

NOWISTHETIME.

Page 42: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Вместофункцииuc($l)можнопоместитьпроизвольныйкод,включаявызовыпрограмм.

Поискn-госовпадения

С помощью модификатора g перебираются все вхождения заданного шаблона. Но то делать, если нужна вполнеопределенная точка совпадения с шаблоном, например, вторая или третья? Оператор цикла while в сочетании скруглымиcкобками,выделяющиминужныйобразец,поможетвам:

$text="Name:AnneNanie:BurkartName:GlaireName:Dan";

while($text=~/Name:\s*(\w+)/g){

++$match;

print"Matchnumber$matchis$1.\n";

}

Matchnumber1isAnne

Matchnumber2isBurkart

Matchnumber3isClaire

Matchnumber4isDan

Этотпримерможнопереписать,используяциклfor:

$text="Name:AnneName:BurkartName:CiaireName:Dan";

for($match=0;

$text=~/Name:\s*(\w+)/g;

print"Matchnumber${\match}is$1.\n")

{}

Matchnuwber1IsAnne

Matchnumber2isBurkart

Matchnumber3isClaire

Matchnumber4isDan

Еслижевамтребуетсяопределитьнужноесовпадениенепономеру,апосодержанию(например,попервойбуквеименипользователя),товместосчетчика$matchможноанализироватьсодержимоепеременнойкаждомнайденномсовпадении.Когдатребуетсяненайти, а заменитьвтороеилитретьевхождениетекста,можноприменитьтужесхему,использоваввкачестветелациклавыражениеperl,вызываемоедлявычислениязаменяющейстроки:

$text="Name:AnneName:BurkartName:ClaireName:Dan";

$match=0;

$text=~s/(Name:\s*(\w+))/#начинаетсякодperl

if(++$match==2)#увеличитьсчетчик

{"Name:John($2)"}#вернутьновоезначение

else{$1}#оставитьстароезначение

/gex;

print$text;

Page 43: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Name:AnneName:John(Burkart)Name:ClaireName:Dan

Впроцессе глобальногопоискаприкаждомнайденномсовпадениивычисляетсявыражение,указанноевкачествевторогооперанда.Приеговычисленииувеличиваетсязначениесчетчика,ивзависимостиотнеговкачествезаменыподставляется либо старое значение текста, либо новое. Модификаторкомментарии,делаякодболеепрозрачным.Обратитевнимание,чтонампришлосьзаключитьвесьшаблонвкруглыескобки,чтобыполучитьзначениенайденноготекстаиподставитьегонапрежнееместополностью.

Какограничить"жадность"квантификаторов

По умолчанию квантификаторы ведут себя как "жадные" объекты. Начиная с текущей позиции поиска, онизахватывают самую длинную строку, которой может соответствовать регулярное выражение, стоящее передквантификатором. Алгоритм перебора с возвратами, используемый perl, способен ограничивать аппетитквантификаторов, возвращаясь назад и уменьшая длину захваченной строки, если не удалось найти соответствиямеждутекстомишаблоном.Однакоэтотмеханизмневсегдаработаеттак,какхотелосьбы.Рассмотримследующийпример.Мыхотимзаменитьтекст"Thatis"текстом"That's".Однаковсилу"жадности"квантификаторарегулярноевыражение".*is"сопоставляетсяфрагментутекстаотначаластрокиидопоследнегонайденного"is":

$text="Thatissometext,isn'tit?";

$text=~s/.*is/That's/;

print$texts;

That'sn'tit?

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

*?-нольилинесколькосовпадений,+?-одноилинесколькосовпадений,??-нольсовпаденийилиодносовпадение,{n}?-ровноnсовпадений,{n,}?-покрайнеймереnсовпадений,{n,m}?-совпаденийпокрайнеймереn,нонеболее,чемm.

Оратите внимание, что смыслквантификатора от этогонеменяется;меняется толькоповедение алгоритмапоиска.Если в процессе сопоставления шаблона и текста прототип определяется однозначно, то алгоритм поиска свозвратамиувеличит"жадность"такогоквантификатораточнотакже,каконограничиваетаппетитсобрата.Однакоесливыборнеоднозначен,торезультатпоискабудетдругим:

$text="Thatissometext,isn'tit?";

$text=~s/.*?is/That's/;

Page 44: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

print$texts;

That'ssometext,isn'tit?

Какудалитьведущиеизавершающиепробелы

Чтобыотсечьотстрокиначальные"пробельныесимволы",можноиспользовать,следующуюкоманду:

$text="Nowisthetime.";

$text=~s/^\s+//;

print$texts;

Nowisthetime.

Чтобыотсечь"хвостовые"пробелы,годитсякоманда:

$text="Nowisthetime.";

$text=~s/\s+$//;

print$texts;

Nowisthetime.

Чтобыотсечьиначальные,ихвостовыепробелылучшевызватьпоследователноэтидвекоманды,чемиспользоватьшаблон, делающий отсечение ненужных пробелов за один раз. Поскольку процедура сопоставления шаблона итекстадостаточносложна,наэтупростуюоперациюможетуйтигораздобольшевремеви,чемхотелосьбы.

Напримервтекстенужнонайтитекст,находящийсямеждуоткрывающимизакрывающимтегом:

$text="<a>blah-blah</a>";

if($text=~m!<([a|b])>(.*?)/\1!ig){

print"$2\n";

}

найдетвсеслова,стоящиемеждутегами<a></a>и<b></b>.

Врегулярныхвыраженияхпристутствуетсвоясемантика:быстрота,торопливостьивозврат.Есликвантификаторсовпадает во многих случаях, то в результате быдет выведен наибольший по длинне результат. Это жадность.Быстрота:поискстараетсянайтикакможнобыстрее."Text"=~/m*/,посмыслусимволоввозвращенозначение0.Т.е.формально0иболеесимволов.

$test="aaooeeooaao";

$test=~s/o*/e/;

print$test;

eaaooeeooaao

Page 45: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

потомучто1элементсторки-0иболеесимволов.

Еслидобавитьквантификаторg,торезультатбудеттаким:

eaeaeeeeeeeeaeaee

т.кстрокасодержит13мест,гдеможетвстречатсяo,втомчислеипустых.

Модификаторы:

/iигнорироватьрегистр/xигнорироватьпропускившаблонеиразрешитькомментарии./gмодификаторразрешающийвыполнениепоиска/заменывезде,гдеэтовозможно/gcнесбрасываетсяпозицияпринеудачномпоиске./sразрешаетсясовпрадение.с\n,игнорируется$*./mразрешитьсовпадение^и$дляначалаиконцастрокивовнутреннихпереводахстрок/oоднократнаякомпиляция/eправаячастьs///представляетсобойвыполняемыйкод/eeправаячастьs///выполняется,послечеговозвращаемоезначениеинтерпретируетсяснова.

привызовеuselocаleучитываютсялокальныенастройки.Модификатор/g= m/(\d+)/g; но это сработает для ненакладывающихся совпадений. Чтобы поймать совпадения нужновоспользоваться оператором ?=... Если ширина = 0, то механизм поиска остался на прежнем месте. Найденыеданныеостаютсявнутрискобок.Еслиестьмодификатор/g,тотекущаяпозицияостаетсяпрежней,нопроисходитперемещение на один символ вперед.$numbers="123456789";@one=$numbers=~/(\d\d\d)/g;@two=$numbers=~/(?=(\d\d\d))/g;print"@one\n";print"@two\n";

Модификаторыmиs нужныдляпоискапоследовательностей символов, содержащихперевод строки.Присовпадает с\n и игнорируется$*.m делает совпадающими^ и $ до и послепрограммныйкод:perl-i-n-p-e's/(.)/lc($1)/g'*.htmlприводитвселитерывовсехфайлахдиректориикнижнемурегистру.

Встроенныепеременныевregex.

$1,$2,$3,$4,...,$n...содержатссылкинанайденныйтекст,тольковтомслучаееслиregexбылвкруглыхскобках:

s%<f(.*?)><(.*?)"><(.*?)">%$1$2$3%g;

внутриregexможноиспользоватьпеременныетипа\1,\2,\3,\4,...\n,...

Page 46: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

s/ahref=(["'])(.*?)\1>/$2/g

найдетвсеурл,заключенныевдвойные,одинарныеивообщебезкавычек,находящиесявдокументе.

для/(a.*b)|(mumu)/впеременной$+содержится$1или$2.

$&содержитполныйтекстсовпаденияприпоследнемпоиске.

$'и$`содержатсястрокидоипослесовпадения

Еслинужноскопироватьисделатьподстановку,тонужнодействоватьпримернотак:

($at=$bt)=~s!m(.*?)o!!#длястрок

for(@mass1=@mass2){s/umka/maugli/}#длямассивов

$u=($m=~s/a/b/g);#поменять$mизанестив$uчислозамен.

Если нужно выцепить только алфавитные символы, с учетом настроек locale, то регексп примерно такой:/^[^\W\d_]+$/внемучитываютсявсенеалфавитныесимволы,нецифрыинеподчеркивания(дляслучая"ванька-встанька"), симвлолотрицаниявгруппе[] -^, т.е.найтивсе,чтоне[\W\d_]!~m/^(\W|\d|_)*/.

Дляупрощенияпониманиясложныхрегулярныхвыраженийможновоспользоватьсяихкомментированием.Иногдаправдаможнотолькоповидурегулярноговыраженияопределитьзачемонопредназначено:

$mmm{$1}=$2while($nnn=~/^([^:]+):\s+(.*)$/m);

читаемрегулярноевыражение:

нужно найти в файле все что до двоеточия не двоеточие и все что после двоеточия(включая возможные

повторенияпослепервого:.*?:.*?:.*?:,потомучтобыланайденаперваяпозиция:выделитьвсечтоне

естьдвоеточиедопервогодвоеточия)

Что это может быть, вполне вероятно, что оно нужно для составления статистики писем, выцепление заголовкаписьмаиегоназванияизmboxвхеш.Покрайнеймереэторегулярноевыражениеподходитдляданнойзадачи.

Рабочиепрограммы,использующиерегулярныевыражения

Впринциперегулярныевыражениеяэтововсеневещьвсебе,хотяиногдаиможетвстретитсязадача,фактическиполностью реализуемая при помощи regex. Ниже приведены программы, иллюстрирующие использование

Page 47: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

реглуярныхвыражений:

Выделениечиселвматематическойзаписи

Пример использования логических условий для нахождения любых чисел в том числе и в общепринятойматематическойзаписи:

#!/usr/bin/perl

$_=qq~

1234

34-4567

3456

-0.35e-0,2

56grf45

-.034E20

-.034e2,01-,045e-,23

-,034e2013e-.20

-,045e-,23e-0.88

4E-0.20

22E-21

-0.2w43

345

2^-,3

~;

print"$1\n"whilem%(([+-]?(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^)

([-+]?\d*[,\.]?)\d+)?)|([+-]?e[+-]?\d*[,.]?\d+))%gxi;

программаисправновыводитвсечисла.Разберемрегулярноевыражение

m%(([+-]?(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^)

([-+]?\d*[,\.]?)\d+)?)|([+-]?e[+-]?\d*[,.]?\d+))%gxi;

впеременной$1 содержится то, что регулярное выражение находит в результате, т.е.([+-]?e[+-]?\d*[,.]?\d+))%gmi нужно для того, чтобы находить числа видаобозначаютдесяткувкакой-тостепени,напримерe-0,20=10-0,20илиE20=10выражение"что-то"длячиселвиданеe20илиE21:

([+-]?(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^)([-+]?\d*[,\.]?)\d+)?)

[+-]? - есть ли в перед числом знак+ или-.? - если вообще есть что-то, находящееся внутри впереди стоящего[...].Выкинемпроверкузнака,регекспсократитсядо

Page 48: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^)([-+]?\d*[,\.]?)\d+)?

рассмотрим regex (?=\d|[\.,]\d)\d* логический оператор (?=B) требует, чтобы перед числов былослучаеB представляетиз себя regex\d|[\.,]\dRegex\d|[\.,]\d значит, чтоперед каждымчисломдолжнобытьчто-толибопросточисло,либочисло,передкоторымстоитлибозапятая,либоточка,т.е.находимвсечиславида.2илипросточисла2(2выбранодляпримера,можетбытьи3).Далеескобказакрываетсяиидет,2точнопройдет(например,2e-,23гдепередзапятойзабылипоставитьнолики,номалолибывает,забыли,надоиэто предусмотреть. Вообще когда пишешь программу, надо предполагать, что е использовать будет ленивыйсклеротическийчайник,правданевсегдавозможнопредугадатьчтоучудитюзер,нокэтомунадостремится),авотчисловида,223непройдет.Даи regex(?=\d|[\.,]\d) говоритотом,чтонужнонайтитолькооднуцифрупослезапятой.Дляостальныхцифринуженквантификатор\d*, которыйзначитлюбоеколичествоцифр,в томчислеиноль,т.е.оноработаетидлячислевида.2или,2Далееидетрегулярноевыражениетом,естьливообщеточкаизапятая(здесьвсюполнуюстрочкувпринципеможноусовершенствовать)ичислотомчислеи егоотсутствие, ведьквантификатор* значитлюбой символв томчислеиноль).Отбрасывая всечтобыловышеотэтогобольшогорегулярноговыраженияостаетсястрочка:

((\se|e|\s?\^)([-+]?\d*[,\.]?)\d+)?

Этастрочкаотвечает запоисквстроке$_математическихобозначенийстепенейтипа0,20напримерa-0,20)ит.д.нотолькодляподстроквида-,034e201.Заметьте,чтовконцестоитзнаквопроса,т.е.если степенное обозначение вообще существует. (\se|e|\s?\^) есть ли числа видачислав"компьютерной"записивида2^-,3=2-0,3,т.е.этимрегекспоммыразрешилипользователюставитьилинеставить пробел при указании степени и разрешили писать значек^ с пробелом перед ним(если есть).Далее идетвыражение([-+]?\d*[,\.]?),котороеговоритотом,чтостепеньможетбытьспоставть нолик, а на самом деле хотел написать a-0,23). Дальше идет цифраквантификатор то *). Потом идет либо точка либо запятая(причем тут негласно введено ограничение наиспользование запятой/точки, после e, если степень дробная или вообще есть, точка или запятая должна быть,инымисловаминеимеетсмысланаписать-2,34e-,23, хотяюзерна самомделехотелнаписатьчислоНаконец мы добрались до конца: идет \d+, но тут уж, пользователь, будь добр напиши хотя бы одно число, т.к.квантификатор+,ане*после\d.Т.е.наложилисвоегородаограниченияздравогосмысла,можнопростонаписатьаможнонаписатьи2e,-чтосутьбессмыленно.Иеще,m%(что-то)%igmстоитквантификаторбытьизаглавнымиквантификаторx,которыйразрешаетразноситьрегулярноевыражениенанесколькострок.

Прошупрошениячтонеставилиногдазнакипрепинания,которыеестьточкаизапятая,тогдаВыбыподумали,чточто-толишнеенаписноинеподсеченокакспецсимволприпомощибэкслэша

Итак,регулярнымвыражением

m%(([+-]?(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^)

Page 49: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

([-+]?\d*[,\.]?)\d+)?)|([+-]?e[+-]?\d*[,.]?\d+))%gxi;

былипредусмотренычисластепенногопорядка,просточисла,числасознаком,нецелыечиславида,3(котороеесть0,3или0.3),ошибкипользователяпривводечисел(типа-.034e2,01хотянадобыписатьлибо-.034e2.01хотяпосмыслупередточкамиизапятыминужноставитьнули,номыпредусмотрелииэто)ичислав"компьютерном"представлении.

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

Облегчениепоискаработы

ДопустимВыоказалисьбезработы,развалиласьвашафирмаилиещекакая-нибудьпричина.Вамтребуетсянайтиновую. Для упрощения этой задачи естьь следующий скрипт, который выцепливает по нужной позиции(вебпрограммирование, зарплатаот200$ит.д.)сwww.job.ruвсезаявкизапоследние10-15дней,точнееемейлы,куданужно слать резюме, что значительно убыстряет поиск работы(имея базы адресов легче разослать одно и то-жерезюме,используянехитрыйсписокрассылки):

#!/usr/bin/perl-wT

$url0="http://www.job.ru/cgi/list1.cgi?GR_NUM=";

$url1="%31&TOPICID=9&EDUC=2&TP=&Gr=&SEX=&AGEMIN=23&AGEMAX=&MONEY=200&CDT=";

$url2="&LDAY=99&ADDR=%ED%CF%D3%CB%D7%C1&KWORD=&KW_TP=AND";

useLWP::Simple;

foreach($i=1;$i<=57;$i++){#57числолистаемыхстраниц

$plus.="%31%2B";

$test=$url0.$plus.$url1.$url2,"\n";

@mass=grep{s/(.*)([\w+\-\.]+\@[\w\-\.]+\.\w{2,3})(.*)/$2/ig}split/\n/,get"$test";

$test.=join"\n",@mass;

$test.=\n";

}

@un=grep{!$test{$_}++}split/\n/,$test;

printjoin"\n",@un;

print"\nВыможетеотправлятьповашейспециальности$#unрезюме\n";

Чтоделаетэтапрограмма,онасоставляетGETзапросизпараметров,которыескрытывhiddenполяхнавигациипорезультатамзапросанаwww.job.ru.ПрограммаприпомощиSimple.pmотправляетзапроснасерверикакбылистаетстранички с поиском. Критерий ваших профессиональных навыков составлен в GET-запросе и осталось толькоразослать почту(для этого можно написать список рассылки) по адресам, которые выдала программа. Разберемрегулярное выражение для вытаскивания почтового адреса из текущей странички

Page 50: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

\.]+\.\w{2,3})(.*)/$2/ig.

[\w+\-\.]\@ - найти все что содержит буквы, тире и точки до символаможетбытьвида[email protected].Тожесамоепослесимвола@-[\w\-\.]+буква от 2 до 3 символов \w{2,3}, т.е. окончание, самый верхний доменвыражениесостоитизтрехклассовскобок(.*)-переменная$1,([\w+\-\.]+\@[\w\-\.]+\.\w{2,3})ивсеостальноев(.*)-$3.Пробелперед$2стоитпотому,чтотакустроенhtml,отдаваемыйпользователюпоискомпо базе предложений о работе www.job.ru. Нам нужно содержимое $2, в котором находится e-mail работодателя.Пишем его во вторую часть s/наш regex/$2/ig. Квантификатор i нужен для того, чтобы не различать регисты[email protected] и [email protected], квантиикатор g задействова на тот случай, если работодатель указывает 2адреса,покоторымнужновысылатьрезюме.На23августа2001годана20часов10минутпрогаммавыдала410e-mailадресов(пролиставза3-4минуты57страниц),гдевасждут,какпотенциальногосотрудника.

Остаетсянаписатьскриптпочтовойрассылкипоe-mails,выданнымданнымскриптом.Ноэтовдругойглаве.

Примеромвышебылполученспсиокemail адресов.Теперьнеобходимопроверить,действительнолисуществуютдомены,накоторыхзаведенытакиепользщователи(примитивная-нопроверка).

#!/usr/bin/perl

useSocket;#загрузитьinet_addr

s{#

(#Сохранитьимяхостав$1

(?:#Группирующиескобки

(?![-_])#ниподчеркивание,нидефис

[\w-]+#кусокименихоста

\.#иточкадомена

)+#повторитьнесколькораз

[A-Za-z]#следующийсимвол-буква

[\w-]+#доменверхнегоуровня

)#конецзаписи$1

}{#Заменитьследующим:

"$1".#исходнчасть+пробел

(($addr=gethostbyname($1))#Еслиимеетсяадрес

?"[".inet_ntoa($addr)."]"#отформатировать

:"[???]"#иначепометитькаксомнительный

)

}gex

Переписываемисходнуюпрограммусучетомвышеприведенногокода

#!/usr/bin/perl-wT

$url0="http://www.job.ru/cgi/list1.cgi?GR_NUM=";

$url1="%31&TOPICID=9&EDUC=2&TP=&Gr=&SEX=&AGEMIN=23&AGEMAX=&MONEY=200&CDT=";

$url2="&LDAY=99&ADDR=%ED%CF%D3%CB%D7%C1&KWORD=&KW_TP=AND";

useSocket;

Page 51: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

useLWP::Simple;

foreach($i=1;$i<=57;$i++){

$plus.="%31%2B";

$test=$url0.$plus.$url1.$url2,"\n";

@mass=grep{s/(.*)([\w+\-\.]+\@[\w+\-\.]+\.\w{2,3})(.*)/$2/ig}split/\n/,get"$test";

$test1.=join"\n",@mass;

$test1.="\n";

}

@res=split/\n/,$test1;

@un=grep{!$test{$_}++}@res;

foreach$file(@un){

$file=~s/(.*)\@(.*)/www\.$2/;

=pod

$file=~s{((?:(?![-_])[\w-]+\.)+[A-Za-z][\w-]+)}

{"$1".(($addr=gethostbyname($1))?"[".inet_ntoa($addr)."]":"[???]")}gex;

print$file,"\n"if($file!~/\?\?\?/);

=cut

$file=~s{

(

(?:

(?![-_])

[\w-]+

\.

)+

[A-Za-z]

[\w_]+

)

}{

"$1".

(($addr=gethostbyname($1))

?"[".inet_ntoa($addr)."]"

:"[???]"

)

}gex;

print$file,"\n"if($file!~/\?\?\?/);

}

Междустрочкамиможнокомментироватьцелыекускикода.

=pod

$file=~s{((?:(?![-_])[\w-]+\.)+[A-Za-z][\w-]+)}

{"$1".(($site=gethostbyname($1))?"[".inet_ntoa($site)."]":"[???]")}gex;

print$file,"\n"if($file!~/\?\?\?/);

=cut

Эта программа успешно удалила некторые из адресов, которые Socket.pm показались подозрительными.Все-такикакую-никакую, а проверку существования e-mail адресс окольными путями при помощи perl провести можно.Авторусеготекставсе-такибольшенравитсявариант,заключенныйвкомментарии=pod(.*?)=cut.Онпростокороче.

Page 52: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

Да и если научится читать сложные регулярные выражения, то можно написать полный регексп е-mail адресов,который занимается тем, что выделяет адреса в точности с соответствующим RFC(занимает это регулярноевыражение несколько страгниц). Но впрочем ниже будет подглава, посвященная чтению монстрообразных, напервый взгляд, регекспов налету, сомножествомпримеров, вышежемы уже попытались угадать предназначениерегулярноговыражениятолькопоеговиду.

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

g-глобальнаязаменае-выполнениеx-улучшенноеформатирование.Еслинаписатьэторегулярноевыражениеводнустрочку,тооноврядлитампоместится:

s{((?:(?![-_])[\w-]+\.)+[A-Za-z][\w-])}#здесьсиловойпереводкаретки

{"$1".(($addr=gethostbyname($1))?"[".inet_ntoa($addr)."]":"[???]")}gex

Разберемодининтересныймоментвданномрегекспе:

s/regex/условие?да:иначе/

Тутпроявляетсяпожалуйоднаиздействительносильнейшихособенностейвыраженииизбежатьмногострочныхусловийсциклом.Вприведенномпримереработаетвсепримернотак:Если$addr=gethostbyname($1) - да, то ставить ip-адрес(inet_ntoa($addr)), если нет(не откликнулся сервер, сбой налинии и пр) то метить этот урл как подозрительный [???]. В принципе в программе ничего человеку делать ненужно, т.к.подозрительныеотметаютсяусловиемprint$file,"\n"if($file!~/\?\?\?/);программы10-15минут.

Оченьпростоерешениедлязеркалановостнойленты

Допустимнужносделатьзеркалокакой-либозарубежнойновостнойлентывместесзагрузкойкартиноксудаленногосервера,чтобынеждатьпонесколькоминутотображениясодержимогополностьюзагруженнойбольшойтаблицы.Приведенныйскриптзапускаетсяприпомощиcrontabкаждые5часов:

#!/usr/bin/perl-w

$/="\001";

print"content-type:text/html\n\n";

$dir="/var/www/docs/html/news/images";

$imgurl="http://www.qwerty.ru/news/images";

useLWP::Simple;

useLWP::UserAgent;

Page 53: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

$page=get"http://www.astronomynow.com";

$page=~s/face="(.*?)"//igs;

&getimg($page);

$page=~s!/images/grafix/listdot.gif!../../listdot.gif!igs;

$page=~s!/images/grafix/spacer.gif!../../spacer.gif!igs;

$page=~s!images/grafix/spacer.gif!../../spacer.gif!igs;

if($page=~m!<TABLEWIDTH="400"BORDER="0"CELLPADDING="0"CELLSPACING="0">(.*?)</TD></TR></TABLE>!igsm){

$file=$1;

&getlink($page);

foreach$names(@res){

$names=~s|.*/||ig;

$file=~s|src="http://(.*?)$names"|src=$imgurl/$names|igs;

}

$html=qq~

<TABLEBORDER="0"CELLPADDING="0"

CELLSPACING="0">

$file

</TD></TR></TABLE>~;

}

openF,">$dir/news.txt";

printF$htmlordie"\n\n\nERROR:$!\n\n\n";

closeF;

subgetimg{

&getlink($_[0]);

foreach$img(@res){

my$res=LWP::UserAgent->new->request(newHTTP::RequestGET=>$img);

if($res->is_success){

$img=~s|.*/||;

open(ABC,">$dir/$img")ordie"\n\n\nERROR:$!\n\n\n";

binmode(ABC);

printABC$res->content;closeABCordie"\n\n\nERROR:$!\n\n\n";

}else{

print$res->status_line;

}

}

return@res;

}

subgetlink{

local$_=$_[0];

push(@res,"http://$2")

whilem{SRC\s*=\s*(["'])http://(.*?)\1\s*(.*?)WIDTH="100"HEIGHT="100"(.*?)>}igs;

return@res;

}

Выводрезультатовпоиска

Предположиместьнеобходимостьподсветитьрезультатыпоискавфайлах,подобнотомукакэтоделаетпоисковик

Page 54: Регулярные выражения в Perl · Регулярные выражения в Perl Стив Холзнер От редактора: Когда мы публиковали

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

$sn=4;

{

local$_=$description1;

print"...$1<fontcolor=red>$3</font>$4..."

while(m/(([\s,\.\n^]*\w*){$sn})(\s*$query\s*)(([\s,\.\n^]*\w+){$sn})/ig);

}

$_="";

Исходная задачасостоитвследующем:вывестипо4словаспередиисзадирезультатапоиска,причемтак,чтобыеслисловонаходитсяпервым,тобудетвидно4словапозадинего.Вточноститакое-жеусловиеидляпоследнегослова.

Соответственноизвидарегекспапонятно,чторазделителямисловмогутбытьсимволысимволпереводакаретки^.Комбинация(\d\d\d){$sn}значитчтонужнонайти3цифрытрираза.