| Содержание | Предисловие | В

Глава 7

    В этой главе:


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

Основные понятия

Регулярное выражение представляет собой образец шаблон

Регулярные выражения используются многими программами, в частности, UNIX-командами, программами grep, sed, awk, ed, vi, emacs и даже различными sh

Основные направления использования регулярных выражений

Если бы нам нужно было найти в каком-то файле все строки, содержащие строку abc, мы могли бы использовать команду

grep abc somefile >results

В этом случае abc — регулярное выражение, которое команда grepВ Perl мы можем превратить строку abc в регулярное выражение, заключив ее между косыми:

if (/abc/) ( print $_;

}

Но что же сверяется с регулярным выражением abc в данном случае? Да наша старая подруга, переменная $_!

В данном примере предполагается, что переменная $_ содержит какую-то строку текста и выводится, если в любом месте этой строки обнаруживается последовательность символов

while (о) (

if (/abc/) { print $_;

> )

А что, если мы не знаем, сколько символов b стоит между а и с? То есть что нужно делать, если мы хотим вывести на экран строку тол

grep "ab*c" somefile >results

(Аргумент, содержащий звездочку, заключен в кавычки, потому что мы не хотим, чтобы shell обработал его так, как будто это метасимв

while (о) {

if (/ab*c/) ( print $_;

) >

Как и в grep, такая запись обозначает последовательность, содержащую символ а, ни одного или более символовДругие варианты сопоставления с образцом мы рассмотрим в разделе "Еще об операции сопоставления" после того, как поговорим обо всех видах регулярных выражений.

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

s/ab*c/def/;

Переменная (в данном случае $_) сопоставляется с регулярным выражением (ab*c).Позже, в разделе "Операция замены", мы рассмотрим множество опций операции замены.

Образцы

Регулярное выражение это образец. Одни части образца обозначают отдельные символы. Другие части соответствуют группам си

Образцы, обозначающие один символ

Самый простой и самый распространенный символ, встречающийся в регулярных выражениях, это одиночный символ, соответствую

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

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

/[abode]

соответствует строка, содержащая любую из первых пяти строчных букв алфавита, тогда как образцу

/[aeiouAEIQU]

соответствует любая из первых пяти гласных, причем как строчных, так и прописных. Если вы хотите вставить в список правую квадратную скобку (]), # обозначает любую цифру

[0-9] # то же самое

[0-9\-] # обозначает цифры 0-9 или знак минус

[a-z0-9] # обозначает любую строчную букву или цифру

[a-zA-ZO-9_] # обозначает любую букву, цифру или знак подчеркивания

Существует также такое понятие, как отрицание класса символов: оно обозначается знаком л, который ставится сразу же за левой скобкой. Такому классу символов соответствует любой симво

["0-9] # обозначает любой нецифровой символ

["aeiouAElOU] # обозначает любую негласную букву

["\"] # обозначает любой символ, кроме символа "

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

Таблица 7.1. Предопределенные классы символов

Конструкция

Эквивалентный класс

Конструкция с отрицанием

Эквивалентный класс с отрицанием

\d (цифра)

\w (обычный символ)

\s (пробельный символ)

[0-9] [a-zA-ZO-9] [ \r\t\n\f]

\d (нецифровые символы)

\w (специальные символы)

\s (непробельный символ)

^0-9] [^a-zA-ZO-9] [" \r\t\n\f]

 

Образцу \d соответствует одна цифра. Образцу \w формально соответст

Приведенные выше конструкции можно использовать при задании других классов символов:

[\da-fA-F] # соответствует одной шестнадцатеричной цифре

Образцы, обозначающие группу символов

Свою истинную силу регулярные выражения показывают, когда вам нужно сказать, например, "один и более из этих символов" или "до пяти из этих символов". Давайте посмотрим, как это делаетс

Последовательность

Первый (и, вероятно, самый неочевидный) образец данного видапоследовательность. Например, образец

Множители

Мы уже встречались со звездочкой (*) в роли образца, обозначающего группу символов. Звездочка обозначает ни одного или более экземпляров стоящего непосредственно пер

Есть еще два образца, работающих подобным образом: знак "плюс" (+), который обозначает один или более экземпляров стоящего непосредственно перед ним символа, и вопросительны

Однако все описанные выше образцы (множители) характеризуются "прожорливостью". Например, если множителю может соответствовать 5-10 символов, то каждый раз он будет выбирать

$_ = "fred xxxxxxxxxx barney";

s/x+/boom/;

всегда заменяет словом boom все символы х (что в результате дает fred boom barney),Если нужно сказать "от пяти до десяти" символов х, можно поставить пять иксов, а затем еще пять, дав после каждого из последних пяти вопросительный знак. Это, однако, выглядит уродливо. Есть более простой способ —Если второе число не указано (например, /х {5, } /), это означает "столько или больше" (в данном случае пять и более), а если выпущена и запятая (например, /х{5}/)

Так, регулярное выражение /а. {5} b/ соответствует букве а, отделенной от буквы bМожно было бы вполне обойтись без *, + и ?, потому что эти образцы полностью эквивалентны образцамЕсли в одном выражении используются два множителя, то "правило прожорливости" дополняется правилом "чем левее, тем прожорливее". Например:

$_ = "а ххх с хххххххх с ххх d";

/a.*c.*d/;

В этом случае первая комбинация ".*" в регулярном выражении соответствует всем символам до второй буквы с, несмотря на то, что пол

Можно заставить любой множитель перестать быть "прожорливым" (т.е. сделать его ленивым), поставив после него вопросительный знак:

$_ = "а ххх с хххххххх с ххх d";

/a.*?c.*d/;

Здесь а. * ? с теперь соответствует минимальному числу символов между а и с, а не максимальному. Это значит, что с образцом совпад

* Конечно, /\d(3}/ соответствует не только трехзначным числам, но и любому числу с количеством знаков больше

строки до первой буквы с, а не до второй. Такой модификатор можно ставить после любого множителя (?,+,* и {m,n}).

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

$_ ° "а ххх се хххххххх ci xxx d";

/a.*ce.*d/;

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

Круглые скобки как способ запоминания

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

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

/fred(.)barney\l/;

соответствует строке, состоящей из слова fred, любого символа, кроме символа новой строки, слова barney

/fred.barney./;

где два обозначенных точками символа могут быть одинаковыми или разными; роли это не играет.

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

* На самом деле для поиска буквы с в первой позиции понадобится больший объем поиска с возвратом в операции *,

то вторая часть (считая левые круглые скобки слева направо) обозначается как \2, третья как \3 и т. д. Например,

/a(.)b(.)c\2d\l/;

обозначает а, какой-то символ (назовем его #1), b, еще один символ (назовем его #2),

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

/а(.*)Ь\1с/;

обозначает а, любое количество символов (даже нуль), b, ту же последовательность символов и, наконец, с. Следовательно, этот образ

Дизъюнкция

Следующая групповая конструкциядизъюнкция, т.е. а | b | с. Это значит, что данный образец со

Что, если бы мы хотели найти songbird или bluebird? Мы могли бы нап

Фиксирование образцов

Некоторые особые виды записи позволяют фиксировать образец относительно позиции в строке, в которой ищется соответствие. Обычно при сопоставлении образец "перемещается" по строке слева

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

/fred\b/; # соответствует слову fred, но не Frederick /\bmo/; # соответству

Аналогичным образом \в требует, чтобы в указанной точке границы слова не было. Например:

/\bFred\B/; # соответствует "Frederick", но не "Fred Flintstone"

Две другие фиксирующие точки требуют, чтобы определенная часть образца стояла рядом с концом строки. Символ л обозначает начало строки, если стоит в месте, где сопоставление с начало

Символ $, как и л, фиксирует образец, но не по началу, а по концу строки. Другими словами, с$ соотв

Поддерживаются и другие фиксирующие точки, включая \А, \2 и упреждающие фиксирующие точки, создаваемые с помощью комбинаций (?=...) и (?!...).

Приоритет

Что произойдет, если объединить а | Ь*? Что будет отыскиваться любое количество символов а или Ь или один символ а и любое количество Ь?

Групповые и фиксированные образцы, как и операции, имеют приоритет. Приоритет образцов (от высшего к низшему) приведен в таблице 7.2.

Таблица 7.2. Приоритет групповых регулярных выражений**

Наименование

Обозначение

Круглые скобки Множители Последовательность и фиксация Дизъюнкция

( ) (?: ) ? + * {m,n} ?? +? *? (m,n}? abc л $ \Z (?= ) (?! )

 

* Или прямо перед символом новой строки в конце строки.

** Некоторые из этих символов в нашей книге не описываются. См. книгу Programming Perl или man-страницу

Согласно этой таблице, специальный символ * имеет более высокий приоритет, чем |.Что, если нам понадобится другое например, "любое число символов а или Ь"? В этом случае нужно просто использовать пару круглых скобок. В нашем примере в скобки нужно закл

а (Ь*).

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

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

abc* # соответствует ab, abc, abcc, abccc, abcccc, и т.д. (abc)*

Еще об операции сопоставления

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

Выбор другого объекта для сопоставления (операция :='•)

Обычно строка, которую нужно сопоставить с образцом, не находится в переменной $_, и помещать ее туда довольно утомительно

Эта переменная указывается справа от знака операции. Выглядит это так:

= "hello world";

$а =~ /^he/; # истина

$а =~ /(.)\1/; # тоже истина (соответствует двум 1)

if ($а =~ /(.)\1/) ( t истина, поэтому проводятся дальнейшие операции

1

Справа от знака операции =~ может стоять любое выражение, которое дает в результате некоторое скалярное строковое значение. Например, <stdin>

if (<STDIN> ==~ /л[y1}/) { # начинаются ли входные данные с буквы у? print "And just what might that r

# и чтобы это мог быть за запрос? <STDIN>; # получить строку со стандартного ввода print "Sorry, I'm unabl

# прошу прощения, но я не могу этого сделать )

В данном случае при помощи <stdin> берется очередная строка со стандартного ввода, которая затем сразу же используется как с

Игнорирование регистра

В предыдущем примере мы указывали образец [yY] для обозначения строчной и прописной буквы у. Если речь идет об очень корот

В некоторых версиях grep флаг -i означает "игнорировать регистр". В Perl

Теперь наш предыдущий пример будет выглядеть так:

print "any last request? ";

if (<STDIN> =~ /"y/i) { # начинаются ли входные данные с буквы у? # да! выполнить какие-то операции

}

Использование другого разделителя

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

$path = <STDIN>; # прочитать путевое имя (вероятно, из find?) if ($path =~ /"VusrVetc/) {

# начинается с /usr/etc... }

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

/''•VusrVetc/ # использование стандартного разделителя косой черты m@^/usr/etc@ #

Если хотите, можете опять использовать косые, например, m/fred/. Таким образом, m —

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

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

$what = "bird";

$sentence = "Every good bird does fly.";

if ($sentence =~ /\b$what\b/) {

print "The sentence contains the word $what!\n";

>

Здесь мы использовали ссылку на переменную для построения операции сопоставления с регулярным выражением \bbird\b/.

* Если этот разделитель левый элемент пары (круглая, фигурная, угловая или квадратная скобка), то закрываю

Вот несколько более сложный пример:

$sentence = "Every good bird does fly.";

print "What should I look for? ";

$what = <STDIN>;

chomp($what) ;

if ($sentence =~ /$what/) ( # нашли! print "I saw $what in $sentence.\n";

} else (

print "nope... didn't find it.\n";

)

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

$what = "[box]";

foreach (qw(in([box] out [box] white [sox] ) ) { if (/\Q$what\E/) {

print "$_ matched!\n";

1 }

Здесь конструкция \Q$what\E превращается в \[box\], в результате че

Специальные переменные, защищенные от записи

После успешного сопоставления с образцом переменным $1, $2, $3 и т.д. присваиваются те же значения, что и \1, \2,\3 и т.д.

$_ = "this is a test";

/(\w+)\W+(\w+)/; # сопоставление первых двух слов

# $1 теперь содержит this, а

Доступ к тем же значениям ($1, $2, $3 и т.д.) можно также получить, использовав операцию сопоставления для соответствующих списков

$_ = "this is a test";

($first, $second) = /(\w+)\W+(\w+)/; # сопоставление первых двух слов # $first теперь содержит this, a $se

К другим предопределенным защищенным от записи переменным относятся: $& (часть строки, совпавшая с регулярным выражением);

$_ = "this is a sample string";

/sa.*le/; # соответствует слову sample внутри строки

# $' теперь содержит "this is a "

# $& теперь содержит "sample"

# $' теперь содержит "string"

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

Операция замены

Мы уже говорили о простейшей форме операции замены: s/ регуляр-ное_выражение/новая_строка/. Пора рассмотреть несколько раз

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

$_ = "foot fool buffoon";

s/foo/bar/g; # $_ теперь содержит "bart barl bufbarn"

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

$_ = "hello, world";

$new = "goodbye";

s/hello/$new/; # заменяет hello на goodbye

Символы сопоставления (метасимволы) в регулярном выражении позволяют выполнять сопоставление с образцом, а не просто с символами, трактуемыми буквально:

$_ = "this is a test";

s/(\w+()/<$l>/g; # $_ теперь содержит "<this> <is> <a> <test>"

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

Суффикс i (перед буквой g или после нее, если она есть) заставляет используемое в операции замены регулярное в

* О влиянии этих переменных на производительность рассказывается в книге Mastering Regular Expressions

г ; Как и в операции сопоставления, можно выбрать другой разделитель, если косая черта неудобна. Для этого просто нужно испо

s#fred#barney#; # заменить fred на barney, как в

Как и при сопоставлении, можно с помощью операции =~ указать другой объект для проведения замены. В этом случае объект должен быть таким, которому можно присвоить скалярное значение,

$which = "this is a test";

$which =~ s/test/quiz/; # $which теперь содержит "this is a quiz"

$someplace[$here] =~ s/left/right/; # заменить элемент массива

$d{"t") =~ s/^/x /; # поставить "х " перед элементом массива

Функции split и join

Регулярные выражения можно использовать для разбивки строки на поля. Это делает функция split. Функция

Функция split

Функция split получает регулярное выражение и строку и ищет в этой строке все экземпляры указанного регулярного выражения.

$line = "merlyn::118:10:Randal:/home/merlyn:/usr/bin/peri";

@fields = split (/:/,$line); # разбить $line, используя в качестве t разде

# теперь @fields содержит ("merlyn","","118","10",

# "Randal","/home/merlyn","/usr/bin/peri")

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

Sfields = split(/:+/, $line);

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

Очень часто приходится разбивать на поля значение переменной $_, поэтому этот случай предлагается по умолчанию:

$ = "some string";

Swords = split (/ /); # то же самое, что и Swords = split(/ /, $_); *

# Или две пары, если используется символ из пары "левая-правая".

При такой разбивке соседние пробелы в разбиваемой строке вызовут появление пустых полей (пустых строк). Лучше использовать образец / +/,

Swords = split; # то же самое, что и (Swords = split(/\s+/, $_) ;

Завершающие строки пустые поля в список, как правило, не включаются. Особой роли это обычно не играет. Решение вроде

$line = "merlyn::118:10:Randal:/home/merlyn:";

($name,$password,$uid,$gid,$gcos,$home,$shell) = split(/:/,$line);

# разбить $line, используя в качестве разделителя двоеточие

просто присваивает переменной $shell нулевое значение (undef), если

Функция join

Функция join берет список значений и "склеивает" их, ставя между элементами списка строку-связку. Выглядит это так:

$bigstring = join($glue,@list);

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

$outline = join(":", @fields) ;

Отметим, что строка-связка это не регулярное выражение, а обычная строка, состоящая из символов общим числом нуль или более.

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

$result = (join "+", "", @fields);

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

$output = join ("\n", @data, "");

* На самом деле образец по умолчанию строка "",

Упражнения

'Ответы к упражнениям даны в приложении А.

1. Постройте регулярное выражение, которое соответствует:

а) минимум одному символу а, за которым следует любое число символов Ь;

б) любому числу обратных косых, за которым следует любое число звездочек (любое число может быть и нулем);

в) трем стоящим подряд копиям того, что содержится в переменной

$whatever;

г) любым пяти символам, включая символ новой строки;

д) одному слову, написанному два или более раз подряд (с возможно изменяющимся пробельным символом), где "слово" определяется как непустая последовательность непробельных символов.

2. а) Напишите программу, которая принимает список слов из stdin и ищет строку, содержащую все пять гласных

$ программа </usr/dict/words

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

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

3. Напишите программу, которая просматривает файл /etc/passwcf* (из stdin),

4. Напишите программу, которая просматривает файл /etc/passwd (из stdin) 5. Повторите последнее упражнение, но с выдачей имен всех пользователей, зарегистрировавшихся под одинаковыми именами. (Совет: в хеше вместо числа экземпляров сохраните спис

* Словарь вашей системы может находиться не в каталоге /usr/dict/words; обратитесь к man-странице

** Если используется NIS, то файл /etc/passwd в вашей системе будет соде



|     Назад     |  

| Содержание | Предисловие | В



Сайт создан в системе uCoz