Образцы
Образцы
Регулярное выражение — это образец. Одни части образца обозначают отдельные символы. Другие части соответствуют группам символов. Сначала мы рассмотрим образцы, соответствующие одному символу, а затем образцы, при помощи которых в регулярном выражении обозначается группа символов.
Образцы, обозначающие один символ
Самый простой и самый распространенный символ, встречающийся в регулярных выражениях, — это одиночный символ, соответствующий самому себе. Другими словами, наличие буквы а в регулярном выражении требует наличия соответствующей буквы а в строке.
Следующий из самых известных символов сопоставления — точка ("."). Точка обозначает любой одиночный символ, кроме символа новой строки (\п). Например, образцу /а. / соответствует любая двухбуквенная последовательность, которая начинается с буквы а и не является последовательностью "а\п".
Класс символов
сопоставления задается списком символов, заключенных в квадратные скобки. Чтобы строка считалась совпавшей с образцом, в соответствующей ее части должен присутствовать один и только один из этих символов. Например, образцу/[abode]
соответствует строка, содержащая любую из первых пяти строчных букв алфавита, тогда как образцу
/[aeiouAEIQU]
соответствует любая из первых пяти гласных, причем как строчных, так и прописных. Если вы хотите вставить в список правую квадратную скобку (]), поставьте перед ней обратную косую или же поставьте эту скобку на первое место в списке. Диапазоны символов (например, от а до z) можно приводить в сокращенной записи, указав конечные точки диапазона через дефис (-). Чтобы включить в список дефис как таковой, поставьте перед ним обратную косую или поместите его в конец. Вот еще несколько примеров:
[0123456789] # обозначает любую цифру
[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 формально соответствует один обычный символ, но на самом деле ему соответствует любой символ, который допустим в именах переменных Perl. Образцу \s соответствует один пробельный символ. К пробельным символам относятся пробел, возврат каретки (редко используемый в UNIX), символ табуляции, символы перехода на новую строку и на новую страницу. Варианты конструкций с 'использованием прописных букв соответствуют дополнениям (отрицаниям) этих классов. Так, \w обозначает один специальный символ, \s — один символ, который не является пробельным (т.е. является буквой, знаком препинания, управляющим символом и т.д.), a \D — один нецифровой символ.
Приведенные выше конструкции можно использовать при задании других классов символов:
[\da-fA-F] # соответствует одной шестнадцатеричной цифре
Образцы, обозначающие группу символов
Свою истинную силу регулярные выражения показывают, когда вам нужно сказать, например, "один и более из этих символов" или "до пяти из этих символов". Давайте посмотрим, как это делается.
Последовательность
Первый (и, вероятно, самый неочевидный) образец данного вида — последовательность. Например, образец abc соответствует букве а, за которой следует буква Ь, за которой идет буква с. Вроде бы просто, но название этому виду образца все равно нужно дать, чтобы в дальнейшем знать, о чем идет речь.
Множители
Мы уже встречались со звездочкой (*) в роли образца, обозначающего группу символов. Звездочка обозначает ни одного или более экземпляров стоящего непосредственно перед ней символа (или класса символов).
Есть еще два образца, работающих подобным образом: знак "плюс" (+), который обозначает один или более экземпляров стоящего непосредственно перед ним символа, и вопросительный знак (?), который обозначает ни одного или один экземпляр стоящего непосредственно перед ним символа. Например, регулярное выражение /fo+ba?r/ обозначает символ f, за которым следует один или более символов о, затем символ Ь, затем ни одного или один символ а и, наконец, символ г.
Однако все описанные выше образцы (множители) характеризуются "прожорливостью". Например, если множителю может соответствовать 5-10 символов, то каждый раз он будет выбирать десятисимвольную строку. Например,
$_ = "fred xxxxxxxxxx barney";
s/x+/boom/;
всегда заменяет словом boom все символы х (что в результате дает fred boom barney), а не только один или два, несмотря на то, что более короткий набор иксов соответствовал бы этому же регулярному выражению.
Если нужно сказать "от пяти до десяти" символов х, можно поставить пять иксов, а затем еще пять, дав после каждого из последних пяти вопросительный знак. Это, однако, выглядит уродливо. Есть более простой способ — применение общего множителя. Общий множитель состоит из пары фигурных скобок, между которыми заключены одно-два числа, например /х{5,10}. Необходимо найти символ, стоящий непосредственно перед скобками (в данном случае это буква х), повторяющийся указанное число раз (в рассматриваемом случае — от пяти до десяти)*.
Если второе число не указано (например, /х {5, } /), это означает "столько или больше" (в данном случае пять и более), а если выпущена и запятая (например, /х{5}/), это означает "ровно столько" (в данном случае пять символов х). Чтобы получить пять или менее символов х, нужно перед запятой поставить нуль: /х {0, 5} /.
Так, регулярное выражение /а. {5} b/ соответствует букве а, отделенной от буквы b любыми пятью символами, кроме символов новой строки, и все это может быть в любом месте строки. (Вспомните, что точка соответствует любому символу, кроме символа новой строки, а нам здесь нужно пять таких символов.) Эти пять символов не обязательно должны быть одинаковыми. (В следующем разделе мы увидим, как заставить их быть одинаковыми.)
Можно было бы вполне обойтись без *, + и ?, потому что эти образцы полностью эквивалентны образцам {0,},(!,} и {0,1}, но проще ввести один эквивалентный знак препинания, к тому же это более привычно.
Если в одном выражении используются два множителя, то "правило прожорливости" дополняется правилом "чем левее, тем прожорливее". Например:
$_ = "а ххх с хххххххх с ххх d";
/a.*c.*d/;
В этом случае первая комбинация ".*" в регулярном выражении соответствует всем символам до второй буквы с, несмотря на то, что положительный результат был бы достигнут даже при совпадении только символов, стоящих до первой буквы с. Сейчас это никакой роли не играет, но позднее, когда нам потребуется анализировать части, совпавшие с регулярным выражением, это будет очень важно.
Можно заставить любой множитель перестать быть "прожорливым" (т.е. сделать его ленивым), поставив после него вопросительный знак:
$_ = "а ххх с хххххххх с ххх d";
/a.*?c.*d/;
Здесь а. * ? с теперь соответствует минимальному числу символов между а и с, а не максимальному. Это значит, что с образцом совпадает часть
* Конечно, /\d(3}/ соответствует не только трехзначным числам, но и любому числу с количеством знаков больше трех. Чтобы задать именно трехзначное число, нужно использовать фиксирующие точки, которые рассматриваются ниже в разделе "Фиксирующие образцы".
строки до первой буквы с, а не до второй. Такой модификатор можно ставить после любого множителя (?,+,* и {m,n}).
Что, если строка и регулярное выражение несколько изменятся, скажем, так:
$_ ° "а ххх се хххххххх ci xxx d";
/a.*ce.*d/;
Символы .* в этом случае соответствуют максимально возможному числу символов, стоящих до следующей буквы с, но очередной символ регулярного выражения (е) не совпадает с очередным символом строки (i). В этом случае мы получаем автоматический поиск с возвратом: поиск начинается сначала и завершается остановкой в некоторой позиции до выбранной на первом этапе (в нашем случае — в позиции предыдущей с, рядом с е)*. Сложное регулярное выражение может включать множество уровней поиска с возвратом, в результате чего время выполнения значительно увеличивается. В данном случае превращение множителя в "ленивый" (с помощью вопросительного знака) упрощает задачу, которую должен выполнить Perl, поэтому рекомендуем хорошо изучить этот метод.
Круглые скобки как способ запоминания
Следующая групповая операция — пара круглых скобок, в которую заключается часть образца. При совпадении с образцом никаких изменений не происходит, просто совпавшая часть строки запоминается, и к ней можно впоследствии обращаться. Например, (а) продолжает соответствовать букве а, а ([a-z] ) — любой строчной букве.
Чтобы вызвать часть строки, которую программа запомнила, нужно поставить обратную косую и целое число. Образец такой конструкции обозначает последовательность символов, обозначенную ранее в паре круглых скобок под тем же номером (считая с единицы). Например,
/fred(.)barney\l/;
соответствует строке, состоящей из слова fred, любого символа, кроме символа новой строки, слова barney и еще одного такого же символа. Таким образом, данному образцу соответствует последовательность символов fredxbarneyx, a не fredxbarneyy. Сравните это с
/fred.barney./;
где два обозначенных точками символа могут быть одинаковыми или разными; роли это не играет.
Откуда взялась единица? Она обозначает первую заключенную в круглые скобки часть регулярного выражения. Если таких частей больше, чем одна,
* На самом деле для поиска буквы с в первой позиции понадобится больший объем поиска с возвратом в операции *, но описание этого процесса не представляет интереса, а работает он по такому же принципу.
то вторая часть (считая левые круглые скобки слева направо) обозначается как \2, третья — как \3 и т. д. Например,
/a(.)b(.)c\2d\l/;
обозначает а, какой-то символ (назовем его #1), b, еще один символ (назовем его #2), с, символ #2, d и символ #1. Таким образом, этот образец соответствует, в частности, строке axbycydx.
Запоминаемая часть может состоять не только из одного символа. Например,
/а(.*)Ь\1с/;
обозначает а, любое количество символов (даже нуль), b, ту же последовательность символов и, наконец, с. Следовательно, этот образец совпадет со строкой aFREDbFREDc и даже со строкой abc, но не со строкой аХХЬХХХс.
Дизъюнкция
Следующая групповая конструкция — дизъюнкция, т.е. а | b | с. Это значит, что данный образец соответствует только одному из указанных вариантов (в данном случае — а, b или с). Такая конструкция работает даже в том случае, если варианты содержат несколько символов, как в образце /song | blue/, что соответствует либо song, либо blue. (Для односимвольных альтернатив определенно лучше будет использовать класс символов, например, / [ abc ] /.)
Что, если бы мы хотели найти songbird или bluebird? Мы могли бы написать /songbird | bluebird/, но часть bird не хотелось бы указывать дважды. Из такой ситуации есть выход, однако вначале нам следует поговорить о приоритете группирующих образцов, который рассматривается ниже, в разделе "Приоритет".
Фиксирование образцов
Некоторые особые виды записи позволяют фиксировать образец относительно позиции в строке, в которой ищется соответствие. Обычно при сопоставлении образец "перемещается" по строке слева направо; сообщение о совпадении дается при первой же возможности. Фиксирующие точки позволяют гарантировать, что с образцом совпадают определенные части сравниваемой строки.
Первая пара фиксирующих директив требует, чтобы определенная часть символов, соответствующих образцу, была расположена либо на границе слова, либо не на границе слова. Фиксирующая директива \Ь требует, чтобы совпадение с образцом b происходило только на границе слова. Граница слова — это место между символами, которые соответствуют предопределенным классам \w или \w, либо между символами, которые соответствуют классу \w, а также начало или окончание строки. Отметим, что все это больше предназначено для работы с С, а не с английскими словами, но вполне применимо и к словам. Например:
/fred\b/; # соответствует слову fred, но не Frederick /\bmo/; # соответствует словам тое и mole, но не Eimo /\bFred\b/; # соответствует слову Fred, но не Frederick или alFred /\b\+\b/; # соответствует "х+у", но не "++" или " + " /abc/bdef/; # никогда не дает совпадения(границы там быть не может)
Аналогичным образом \в требует, чтобы в указанной точке границы слова не было. Например:
/\bFred\B/; # соответствует "Frederick", но не "Fred Flintstone"
Две другие фиксирующие точки требуют, чтобы определенная часть образца стояла рядом с концом строки. Символ л обозначает начало строки, если стоит в месте, где сопоставление с началом строки имеет смысл. Например, "а соответствует символу а в том и только в том случае, если а — первый символ в строке, aл соответствует двум символам, а и л, стоящим в любом месте строки. Другими словами, символ л утратил свое специальное значение. Если вы хотите, чтобы он имел буквальный смысл и в начале строки, поставьте перед ним обратную косую черту.
Символ $, как и л, фиксирует образец, но не по началу, а по концу строки. Другими словами, с$ соответствует символу с только в том случае, если он стоит в конце строки*. Знак доллара в любом другом месте образца, вероятно, будет интерпретироваться как представление скалярного значения, поэтому для того, чтобы использовать его в строке буквально, перед ним следует поставить обратную косую.
Поддерживаются и другие фиксирующие точки, включая \А, \2 и упреждающие фиксирующие точки, создаваемые с помощью комбинаций (?=...) и (?!...). Они подробно описаны в главе 2 книги Programming Perl и на man-странице perlre(Y).
Приоритет
Что произойдет, если объединить а | Ь*? Что будет отыскиваться — любое количество символов а или Ь или один символ а и любое количество Ь?
Групповые и фиксированные образцы, как и операции, имеют приоритет. Приоритет образцов (от высшего к низшему) приведен в таблице 7.2.
Таблица 7.2. Приоритет групповых регулярных выражений**
Наименование | Обозначение |
Круглые скобки Множители Последовательность и фиксация Дизъюнкция | ( ) (?: ) ? + * {m,n} ?? +? *? (m,n}? abc л $ \А \Z (?= ) (?! ) |
* Или прямо перед символом новой строки в конце строки.
** Некоторые из этих символов в нашей книге не описываются. См. книгу Programming Perl или man-страницу perlreii(l).
Согласно этой таблице, специальный символ * имеет более высокий приоритет, чем |. В силу этого /а |Ь*/ интерпретируется как один символ а или любое число символов ь.
Что, если нам понадобится другое — например, "любое число символов а или Ь"? В этом случае нужно просто использовать пару круглых скобок. В нашем примере в скобки нужно заключить ту часть выражения, к которой должна относиться *, т.е. (а|Ь)*. Если вы хотите подчеркнуть, какое выражение вычисляется первым, можно дать избыточные круглые скобки:
а (Ь*).
Изменение приоритета с помощью круглых скобок одновременно активизирует режим запоминания для данного образца, как мы рассказывали выше. То есть эти круглые скобки учитываются, когда вы определяете, соответствует ли какой-то элемент \2, \3 и т.д. Если вы хотите использовать круглые скобки без включения режима запоминания, применяйте форму (?:...), а не (...). Она тоже позволяет указывать множители, но не изменяет значение счетчика подлежащих запоминанию лексем, используя, например, переменную $4 и т.п. Например,/(?: Fred |Wilma) Flintstone/ ничего не записывает в переменную $ 1; здесь просто предполагается группирование.
Вот еще несколько примеров регулярных выражений и действия круглых скобок:
abc* # соответствует ab, abc, abcc, abccc, abcccc, и т.д. (abc)* # соответствует "", ab, abc, abcabc, abcabcabc, и т.д. ^х |у # соответствует х в начале строки или у в любом месте л^x.^y) # соответствует х или у в начале строки а|be Id # либо а, либо be, либо d (alb)(с Id) # ас, ad, be или bd (song|blue)bird # songbird или bluebird