Саммерфилд - Программирование на Python 3 (1077331), страница 119
Текст из файла (страница 119)
Другое выражение: 1077 соответствует нулевому или большему числу цифр, но предпочтение будет отдано нулевому числу совпадений, потому что используется минимальный квантификатор — он порождает ту же проблему, что и квантификатор *, и может находить соответствие «с ничем», то есть будет соответствовать вообще любому тексту. Минимальные квантификаторы могут с успехом использоваться для быстрого и приблизительного синтаксического анализа документов в формате ХМ) и НТМ1 . Например, для поиска всех тегов изображе- Глава !2.
Регулярные выражения ний можно было бы использовать выражение <1ад,*> (соответствует одному символу «<», за которым следует символ «1», затем символ «з», затем символ «д», затем ноль или более любых символов, за исключением символа перевода строки„и затем символ «>»), но оно будет давать неверные результаты, потому часть .* является жадной и будет соответствовать всему подряд, включая закрывающую угловую скобку тега «>», и остановится только по достижении последнего символа «>» в тексте. Сами собой напрашиваются три варианта решения (кроме использования нормального парсера).
Одно из них — выражение <1ад[ ">]*> (соответствует подстроке <1зд, за которой следует любое число любых символов, отличных от >, и затем следует закрывающая угловая скобка тега >), другое — выражение <1зд. *2> (соответствует подстроке <!пд, за которой следует любое минимальное число любых символов, поэтому поиск соответствия будет остановлен сразу же по достижении символа >, затем следует >) и третье решение является комбинацией двух предыдущих — <1ад[ >] 2>.
Но ни одно из них не является правильным, потому что все они будут соответствовать строке <1зд>, которая не является допустимым тегом. Так как известно, что тег изображения должен иметь атрибут э гс, можно записать более точное регулярное выражение <!ад~э+[ ">]*2згс»~и+[ ">]*2>. Это выражение соответствует всем литералам символов <!зд, за которыми следует один или более пробельных символов, затем минимально ноль или более любых символов, за исключением > (чтобы пропустить любые атрибуты, такие как а1!), затем атрибут э гс (символы з го=, затем хотя бы один символ «слова»), затем любой (включая нулевое число) символ, за исключением >, чтобы пропустить любые другие атрибуты, и, наконец, закрывающая угловая скобка >.
Группировка и сохранение На практике часто бывают необходимы регулярные выражения, соответствующие любой из двух или более альтернатив, и нередко требуется сохранить совпадение или какую-то его часть для последующей обработки. Кроме того, иногда требуется, чтобы квантификатор применялся к нескольким выражениям. Все это может быть реализовано с помощью операции группировки (), а выбор альтернативных вариантов можно реализовать с помощью конструкции выбора ~.
Конструкция выбора особенно удобна, когда необходимо отыскать совпадение с одной из нескольких альтернатив. Например, регулярное выражение з1го гз21 (з1гр1зпе (] ег будет соответствовать любому тексту, содержащему «а!гога!», «а!гр1апе» или «)е1». Тот же результат можно получить с помощью регулярного выражения з1г(сгз211р1зпе) !]зг. В данном случае круглые скобки используются для группировки выражений, таким образом здесь имеются два внешних выражения: з1г(сгзгт!р1зпе) и ]ет. Первое из них имеет внутреннее выражение Язык регулярных выражений в Ругпоп 531 сгвй ~ р1апе, а так как ему предшествует выражение а1г, первое внешнее выражение может соответствовать только слову «а1гсгай» или «шгр1апе».
Круглые скобки преследуют две разные цели — сгруппировать выражения и сохранить текст, совпавший с выражением. Мы будем использовать термин группировка для обозначения сгруппированного выражения как выполняющего сохранение, так и не выполняющего, и сохранение или группировка с сохранениезг — для обозначения сохраняющей группы. Регулярное выражение (а1гсгай) а1гр1апе1)ет) не только будет обнаруживать соответствие любому из трех выражений, но еще будет сохранять любое совпадение для последующего использования. Сравните это с регулярным выражением (а1г(огай ~ р1впв) ()ег), в котором будет сохранено два фрагмента, если будет обнаружено совпадение с первым выражением («а1гсгай» или «а1гр1апе» вЂ” первое сохранение, и «огай» или «р1апе» вЂ” второе сохранение), и одно сохранение, если будет обнаружено совпадение со вторым выражением («)е1»). Имеется возможность отключить эффект сохранения, поместив вслед за открывающей круглой скобкой символы?:; так, например, регулярное выражение (а1г(г:огай(р1апе) Пе1) при любом совпадении будет иметь только одно сохранение («а1гсгай», или «а1гр1апе» или «)е1»).
Сгруппированное выражение — это обычное выражение, и потому к нему могут применяться квантификаторы. Как и для любого другого выражения, отсутствие квантификатора подразумевает точно одно совпадение. Например, при чтении текстового файла со строками в формате ключ=значение, где ключ содержит только алфавитно-цифровые символы, регулярное выражение (~в+) =(, +) будет соответствовать любой строке, в которой присутствуют непустой ключ и непустое значение.
(Не забывайте, что . соответствует всему, чему угодно, за исключением символов перевода строки.) И для каждой совпавшей строки будет выполнено два сохранения. Первым сохранением будет ключ, а вторым — значение. Например, регулярному выражению поиска пар ключ-значение будет соответствовать вся строка 1с йс= П в1са1 ео гв П с двумя сохранениями, выделенными серым фоном. Обратите внимание, что второе сохранение включает в себя несколько пробельных символов и что пробельный символ перед знаком = является недопустимым. Чтобы обеспечить большую гибкость в отношении пробелов и одновременно удалить нежелательные пробелы из сохранений, мы могли бы усовершенствовать регулярное выражение, создав более длинную версию: ( 1г)*(1н«)( 1«)*=( 1«)*(.») Этому регулярному выражению будет соответствовать та же строка, что и прежде, а также строки, где символ = с обеих сторон окружен пробелами; но в первом сохранении будут отсутствовать начальные и завершающие пробелы, а во втором сохранении будут отсутствовать начальные пробелы.
Например: Го 1с = П з1св1' во гв П . Глава)2. Регулярные выражения Мы сделали все возможное, чтобы обеспечить совпадение с пробельными символами за пределами сохраняющих круглых скобок и одновременно обеспечить соответствие строк, в которых вообще отсутствуют пробелы. Мы не использовали символ ~в для поиска совпадений с пробельными символами, потому что ему также соответствует символ перевода строки (~л); в противном случае это могло бы привести к некорректному совпадению сразу с несколькими строками (например, при использовании флага ге. МОП1Е1НЕ). И для поиска значения мы не использовали символ ~8, соответствующий непробельным символам, потому что мы допускаем наличие пробелов внутри значений (например, внутри текста предложения). Чтобы исключить завершающие пробелы из второго сохранения, нам потребовалось бы более сложное регулярное выражение — мы увидим его в следующем подразделе.
Обратиться к захваченному тексту можно с помощью обратных ссылок, то есть с помощью ссылок на предшествующие группы.' Синтаксически обратные ссылки в регулярных выражениях имеют вид ~~, где а — это порядковый номер сохраняющей группы. Нумерация сохраняющих групп начинается с единицы и увеличивается на единицу с каждой новой (начинающей сохраняющую группу) открывающей круглой скобкой, слева направо. Например, в упрощенном варианте поиск повторяющихся слов можно было бы выполнять с помощью регулярного выражения (~и+)~в+~2, которому соответствует «слово», за которым следует по меньшей мере один пробельный символ и затем то же самое слово, что было захвачено.
(Сохранение с порядковым номером О создается автоматически, без применения круглых скобок — оно хранит все соответствие, то есть то, что мы показываем шрифтом с подчеркиванием.) Позднее мы увидим более сложный способ поиска повторяющихся слов. В длинных или сложных регулярных выражениях для ссылки на сохранения часто удобнее использовать имена, а не порядковые номера. Это позволяет упростить сопровождение, так как добавление или удаление сохраняющих круглых скобок может приводить к изменению номеров, но не будет оказывать влияния на имена.
Чтобы присвоить имя сохраняющей группе, вслед за открывающей круглой скобкой следует указать 2Р<лаие>. Например, в регулярном выражении (2Р<кву> ~и+)»(2Р<ча1се>. +) имеется две сохраняющих группы — с именами тяеу" и "ча1зе". Для обращения к именованным сохраняющим группам внутри регулярных выражений используется синтаксис (2Р=лаие). На- Обратите внимание, что обратные ссылки ие могут использоваться внутри символьных классов, то есть внутри (). 533 Язык регулярных выражений в Ру[Ьоп пример, регулярному выражению (?Р<вого>хи+)хв»(?Р=вого), где используется сохраняющая группа с именем ?нпгс", будут соответствовать пары повторяющихся слов. Проверки и флаги Одна из проблем, которая свойственна многим регулярным выражениям, которые мы видели выше, заключается в том, что они могут находить больше соответствий, чем предполагается, и даже могут находить соответствия там, где они не предполагались.
Например, регулярному выражению в1гсга?1 |в1гр1впе|]ег будут соответствовать слова «зча1ег)е1» и «)е(вЫ», кроме «Зе(». Такого рода проблемы могут быть решены с помощью проверки. Проверка не соответствует никакому тексту, она просто требует выполнения некоторого условия в точке, где выполняется проверка. Проверка хв (граница слова) требует, чтобы символ, предшествующий ей, был символом «слова» (~и), а символ, следующий за ней, не был символом «слова» (хп), и наоборот. Например, регулярное выражение ]ет будет находить в тексте гяе )ет апо' ]етвхз аге позву два соответствия: гве ]еС апе ]етвкз аге по1ву, а регулярное выражение ХЬ]е?~Ь будет находить только одно соответствие: гпе ]ет апз )егвх1 аге позву.
Возвращаясь к оригинальному регулярному выражению, мы могли бы записать его как хьв1гсгв71хь! ~ьв1гр1впв~ь!~ь]егерь или, более просто,— как хь(7:в1гсгвгг]а1гр1впв|]ег)хь, то есть граница слова, несохраняющее выражение, граница слова. Поддерживаются многие другие проверки, как показано в табл. 12.3. Мы могли бы использовать проверки для улучшения регулярного выражения, отыскивающего пары ключ=значение, например, заменив его выражением (Хв+)»(["'Хп]+) и установив флаг ге.
М[)[Т[[]МЕ, чтобы гарантировать, что каждая пара ключ=значение будет извлекаться из единственной строки и не будет происходить объединения нескольких строк.(Перечень флагов приводится в табл. 12.4 на стр. 53б,их синтаксис описывается в конце етого подраздела, а примеры использования — в начале следующего раздела.) А если бы нам потребовалось удалять начальные и конечные пробельные символы и использовать именованные сохраняющие группы, то полное регулярное выражение могло бы выглядеть так: [ 'хг] (?Р<хеу>хя»)[ ~1]*=[ 'хг]*(?Р<чв1ие>[ 'хп]«)(7<! [ 'хт]) Хотя это регулярное выражение решает очень простую задачу„оно выглядит достаточно сложным. Один из способов упростить его сопровождение состоит в использовании комментариев.
Сделать зто можно, добавляя встроенные комментарии, используя синтаксис [Рятекст комментария), но на практике такие комментарии легко могут еще больше осложнить понимание регулярного Глава 12. Регулярные выражения [ 'хт)* З начало строки и необязательные начальные пробелы (2Р<хеу>хив) 3 текст ключа [ хт) =[ Чт) з энзк равенства с еозиокныии пробелами, окружающими его (2Р<уз1ые>["Хп)+) З текст значения (2<1[ 'хт)) З негативная ретроспектиензя проверка для исключения з ззеерюающих пробелов Таблица 12.3.