Программирование на Python 3. Руководство издательство СимволПлюс
![]()
|
air(craft|plane) и jet. Первое из них имеет внутреннее выражение Язык регулярных выражений в Python 531 craft|plane , а так как ему предшествует выражение air, первое внеш нее выражение может соответствовать только слову «aircraft» или «airplane». Круглые скобки преследуют две разные цели – сгруппировать выраже ния и сохранить текст, совпавший с выражением. Мы будем использо вать термин группировка для обозначения сгруппированного выраже ния как выполняющего сохранение, так и не выполняющего, и сохра нение или группировка с сохранением – для обозначения сохраняю щей группы. Регулярное выражение (aircraft|airplane|jet) не только будет обнаруживать соответствие любому из трех выражений, но еще будет сохранять любое совпадение для последующего использования. Сравните это с регулярным выражением (air(craft|plane)|jet), в кото ром будет сохранено два фрагмента, если будет обнаружено совпаде ние с первым выражением («aircraft» или «airplane» – первое сохране ние, и «craft» или «plane» – второе сохранение), и одно сохранение, ес ли будет обнаружено совпадение со вторым выражением («jet»). Име ется возможность отключить эффект сохранения, поместив вслед за открывающей круглой скобкой символы ?:; так, например, регуляр ное выражение (air(?:craft|plane)|jet) при любом совпадении будет иметь только одно сохранение («aircraft», или «airplane» или «jet»). Сгруппированное выражение – это обычное выражение, и потому к не му могут применяться квантификаторы. Как и для любого другого вы ражения, отсутствие квантификатора подразумевает точно одно сов падение. Например, при чтении текстового файла со строками в фор мате ключ=значение, где ключ содержит только алфавитноцифровые символы, регулярное выражение (\w+)=(.+) будет соответствовать лю бой строке, в которой присутствуют непустой ключ и непустое значе ние. (Не забывайте, что . соответствует всему, чему угодно, за исклю чением символов перевода строки.) И для каждой совпавшей строки будет выполнено два сохранения. Первым сохранением будет ключ, а вторым – значение. Например, регулярному выражению поиска пар ключ=значение будет соответствовать вся строка = с двумя сохране ниями, выделенными серым фоном. Обратите внимание, что второе сохранение включает в себя несколько пробельных символов и что пробельный символ перед знаком = является недопустимым. Чтобы обеспечить большую гибкость в отношении пробелов и одновременно удалить нежелательные пробелы из сохранений, мы могли бы усовер шенствовать регулярное выражение, создав более длинную версию: [ \t]*(\w+)[ \t]*=[ \t]*(.+) Этому регулярному выражению будет соответствовать та же строка, что и прежде, а также строки, где символ = с обеих сторон окружен пробелами; но в первом сохранении будут отсутствовать начальные и завершающие пробелы, а во втором сохранении будут отсутствовать начальные пробелы. Например: = topic physical geography topic physical geography 532 Глава 12. Регулярные выражения Мы сделали все возможное, чтобы обеспечить совпаде ние с пробельными символами за пределами сохраняю щих круглых скобок и одновременно обеспечить соот ветствие строк, в которых вообще отсутствуют пробелы. Мы не использовали символ \s для поиска совпадений с пробельными символами, потому что ему также соот ветствует символ перевода строки (\n); в противном слу чае это могло бы привести к некорректному совпадению сразу с несколькими строками (например, при использо вании флага re.MULTILINE). И для поиска значения мы не использовали символ \S, соответствующий непробель ным символам, потому что мы допускаем наличие пробе лов внутри значений (например, внутри текста предло жения). Чтобы исключить завершающие пробелы из второго сохранения, нам потребовалось бы более слож ное регулярное выражение – мы увидим его в следую щем подразделе. Обратиться к захваченному тексту можно с помощью обратных ссы лок , то есть с помощью ссылок на предшествующие группы. 1 Синтак сически обратные ссылки в регулярных выражениях имеют вид \i, где i – это порядковый номер сохраняющей группы. Нумерация со храняющих групп начинается с единицы и увеличивается на единицу с каждой новой (начинающей сохраняющую группу) открывающей круглой скобкой, слева направо. Например, в упрощенном варианте поиск повторяющихся слов можно было бы выполнять с помощью ре гулярного выражения (\w+)\s+\1, которому соответствует «слово», за которым следует по меньшей мере один пробельный символ и затем то же самое слово, что было захвачено. (Сохранение с порядковым номе ром 0 создается автоматически, без применения круглых скобок – оно хранит все соответствие, то есть то, что мы показываем шрифтом с подчеркиванием.) Позднее мы увидим более сложный способ поиска повторяющихся слов. В длинных или сложных регулярных выражениях для ссылки на со хранения часто удобнее использовать имена, а не порядковые номера. Это позволяет упростить сопровождение, так как добавление или уда ление сохраняющих круглых скобок может приводить к изменению номеров, но не будет оказывать влияния на имена. Чтобы присвоить имя сохраняющей группе, вслед за открывающей круглой скобкой следует указать ?P<name>. Например, в регулярном выражении (?P \w+)=(?P имеется две сохраняющих группы – с именами "key" и "value". Для обращения к именованным сохраняющим группам внутри регулярных выражений используется синтаксис (?P=name). На 1 Обратите внимание, что обратные ссылки не могут использоваться внутри символьных классов, то есть внутри []. Флаги регулярных выражений, стр. 535 Язык регулярных выражений в Python 533 пример, регулярному выражению (?P пользуется сохраняющая группа с именем "word", будут соответство вать пары повторяющихся слов. Проверки и флаги Одна из проблем, которая свойственна многим регулярным выраже ниям, которые мы видели выше, заключается в том, что они могут на ходить больше соответствий, чем предполагается, и даже могут нахо дить соответствия там, где они не предполагались. Например, регу лярному выражению aircraft|airplane|jet будут соответствовать слова «waterjet» и «jetski», кроме «jet». Такого рода проблемы могут быть решены с помощью проверки. Проверка не соответствует никакому тексту, она просто требует выполнения некоторого условия в точке, где выполняется проверка. Проверка \b (граница слова) требует, чтобы символ, предшествующий ей, был символом «слова» (\w), а символ, следующий за ней, не был символом «слова» (\W), и наоборот. Например, регулярное выражение jet будет находить в тексте the jet and jetski are noisy два соответст вия: the jet and jetski are noisy, а регулярное выражение \bjet\b бу дет находить только одно соответствие: the jet and jetski are noisy. Возвращаясь к оригинальному регулярному выражению, мы могли бы записать его как \baircraft\b|\bairplane\b|\bjet\b или, более просто, – как \b(?:aircraft|airplane|jet)\b, то есть граница слова, несохраняю щее выражение, граница слова. Поддерживаются многие другие проверки, как показано в табл. 12.3. Мы могли бы использовать проверки для улучшения регулярного вы ражения, отыскивающего пары ключ=значение, например, заменив его выражением ^(\w+)=([^\n]+) и установив флаг re.MULTILINE, чтобы гарантировать, что каждая пара ключ=значение будет извлекаться из единственной строки и не будет происходить объединения нескольких строк. (Перечень флагов приводится в табл. 12.4 на стр. 535, их син таксис описывается в конце этого подраздела, а примеры использова ния – в начале следующего раздела.) А если бы нам потребовалось уда лять начальные и конечные пробельные символы и использовать име нованные сохраняющие группы, то полное регулярное выражение могло бы выглядеть так: ^[ \t]*(?P Хотя это регулярное выражение решает очень простую задачу, оно выглядит достаточно сложным. Один из спо собов упростить его сопровождение состоит в использова нии комментариев. Сделать это можно, добавляя встро енные комментарии, используя синтаксис (?#текст ком ментария) , но на практике такие комментарии легко мо гут еще больше осложнить понимание регулярного Флаги регулярных выражений, стр. 535 534 Глава 12. Регулярные выражения выражения. Лучшее решение заключается в использовании флага re.VERBOSE – он позволяет использовать внутри регулярных выраже ний пробельные символы и обычные комментарии языка Python с од ним ограничением – когда необходимо описать совпадение с пробель ным символом, следует использовать либо символ \s, либо символь ный класс, такой как [ ] . Ниже приводится регулярное выражение, отыскивающее пары ключ=значение, с комментариями: ^[ \t]* # начало строки и необязательные начальные пробелы (?P [ \t]*=[ \t]* # знак равенства с возможными пробелами, окружающими его (?P (? # завершающих пробелов Таблица 12.3. Проверки регулярных выражений Символ Значение ^ Соответствует началу текста; кроме того, с фла гом re.MULTILINE соответствует позиции сразу по сле каждого символа перевода строки $ Соответствует концу текста; кроме того, с флагом re.MULTILINE соот ветствует позиции перед каждым символом перевода строки \A Соответствует началу текста \b Соответствует границе «слова»; поведение проверки зависит от фла га re.ASCII – внутри символьных классов обозначает символ забоя (backspace) \B Соответствует границе не«слова»; поведение проверки зависит от флага re.ASCII \Z Соответствует концу текста (?=e) Совпадение обнаруживается, если текст справа от позиции провер ки соответствует выражению e, при этом изменение позиции поиска в тексте не происходит – эта проверка называется опережающей про веркой , или позитивной опережающей проверкой (?!e) Совпадение обнаруживается, если текст справа от позиции провер ки не соответствует выражению e, при этом изменение позиции по иска в тексте не происходит – эта проверка называется негативной опережающей проверкой (?<=e) Совпадение обнаруживается, если текст слева от позиции проверки соответствует выражению e, эта проверка называется позитивной ретроспективной проверкой (?e) Совпадение обнаруживается, если текст слева от позиции проверки не соответствует выражению e, эта проверка называется негатив ной ретроспективной проверкой Флаги регулярных выражений, стр. 535 Язык регулярных выражений в Python 535 Таблица 12.4. Флаги модуля регулярных выражений В контексте программирования на языке Python регу лярные выражения, подобные этому, обычно записыва ются в виде «сырых» строк, заключенных в тройные ка вычки, – «сырые» строки используются, чтобы избе жать необходимости дублировать символы обратного слеша, а тройные кавычки – чтобы регулярное выраже ние можно было записывать в нескольких строках. В дополнение к проверкам, обсуждавшимся до сих пор, существуют дополнительные проверки, которые заглядывают по тексту вперед (или назад) от точки проверки, чтобы узнать, соответствует ли (или не соответствует) определенное нами выражение. Выражения, которые могут использоваться в ретроспективных проверках, должны иметь фиксированную длину (то есть в них не могут использоваться кванти фикаторы ?, + и *, а интервальные квантификаторы должны задавать фиксированный размер, например, {3}). В случае регулярного выражения, отыскивающего совпадения со стро ками в формате ключ=значение, негативная обратная проверка для совпадения требует, чтобы символы, предшествующие позиции про верки, не были пробелами или символами табуляции. Вследствие это го последний символ «значения» сохраняется в сохраняющей группе, а завершающие пробелы и символы табуляции – нет (это не относится к пробелам и символам табуляции внутри сохраненного текста). Рассмотрим другой пример. Предположим, что мы читаем много строчный текст, содержащий имена «Helen Patricia Sharman», «Jim Sharman», «Sharman Joshi», «Helen Kelly» и т. д., и нам необходимо отыскать текст «Helen Patricia», но только если он относится к имени Флаг Значение re.A или re.ASCII При наличии этого флага проверки \b, \B, \s, \S, \w и \W действуют так, как если бы они применялись к тексту, содержащему только символы ASCII; по умолчанию действие этих проверок основано на спе цификации Юникода re.I или re.IGNORECASE Поиск совпадений выполняется без учета рагистра символов re.M или re.MULTILINE При наличии этого флага символ ^ соответствует нача лу текста и позиции сразу же после каждого символа перевода строки, а символ $ соответствует позиции пе ред каждым символом перевода строки и концу текста re.S или re.DOTALL При наличии этого флага символ . соответствует лю бому символу, включая символ перевода строки re.X или re.VERBOSE Позволяет включать в регулярные выражения пробе лы и комментарии «Сырые» строки, стр. 85 536 Глава 12. Регулярные выражения «Helen Patricia Sharman». Простейший способ заключается в том, что бы использовать регулярное выражение \b(Helen\s+Patricia)\s+Shar man\b . Однако того же самого можно добиться с помощью опережаю щей проверки, например, \b(Helen\s+Patricia)(?=\s+Sharman\b). Этому выражению будет соответствовать текст «Helen Patricia», только если он предшествует границе слова, за которым следуют пробельные сим волы и слово «Sharman» и далее следует граница слова. Чтобы сохранить определенное имя в самых разных его разновидно стях («Helen», «Helen P.» или «Helen Patricia»), можно было бы соз дать немного более сложное регулярное выражение, например, \b(He len(?:\s+(?:P\.|Patricia))?)\s+(?=Sharman\b) . Ему соответствует грани ца слова, за которой следует имя в одной из форм, но только если вслед за ним следует некоторое число пробельных символов, затем слово «Helen Patricia Sharman» и граница слова. Обратите внимание, что сохранение выполняется только двумя син таксическими конструкциями – (e) и (?P<name>e). Других форм сохра няющих группировок в круглых скобках не существует. Это особенно важно для опережающих и ретроспективных проверок, так как они лишь проверяют последующий или предшествующий текст – сами они не являются частью совпадения, а только проверяют факт наличия или отсутствия совпадения. Это также важно для последних двух син таксических конструкций в круглых скобках, которые мы теперь рас смотрим. Ранее мы уже видели, что получить доступ к сохраненному тексту внутри регулярного выражения можно либо по номеру группы (напри мер, \1), либо по имени (например, (?P=name)). Однако имеется возмож ность принимать решение о наличии соответствия в зависимости от наличия предыдущего совпадения. Для этого используются синтакси ческие конструкции (?(id)yes_exp) и (?(id)yes_exp|no_exp). Здесь id – это имя или номер предыдущей сохраняющей группы. Если для ука занной группы совпадение было обнаружено, здесь будет выполняться проверка на совпадение с выражением yes_exp. Если для указанной группы совпадение не было обнаружено, то здесь будет выполняться проверка на совпадение с выражением no_exp, если оно определено. Рассмотрим пример. Предположим, что необходимо извлечь имена фай лов, которые упоминаются в атрибутах src тегов img в документе HTML. Для начала попробуем просто отыскать атрибут src, но, в отличие от предыдущих попыток, мы учтем тот факт, что значение атрибута мо жет указываться в трех формах: в апострофах, в кавычках и без кавы чек. Вот первая попытка: src=(["'])([^"'>]+)\1. Часть ([^"'>]+) сохра няет найденное совпадение максимальной длины, содержащее по меньшей мере один символ, который не является кавычкой, апостро фом или >. Это регулярное выражение прекрасно работает, когда име на файлов указываются в кавычках, а благодаря обратной ссылке \1 соответствие будет обнаружено, только если кавычки, обрамляющие Язык регулярных выражений в Python 537 имя файла, – одного типа. Но это выражение не позволяет отыскивать имена файлов без кавычек. Чтобы устранить эту проблему, необходи мо сделать символ открывающей кавычки необязательным, а совпаде ние с закрывающей кавычкой выполнять, только если было найдено совпадение с открывающей кавычкой. Вот как выглядит исправлен ное регулярное выражение: src=(["'])?([^"'>]+)(?(1)\1). Здесь мы не используем выражение no_exp, потому что нам не требуется выявлять совпадение еще с чемто, если открывающая кавычка отсутствует. Те перь мы готовы поместить созданное регулярное выражение в опреде ленный контекст. Ниже приводится полное регулярное выражение, в котором используются именованные группы и комментарии: [^>]*? # любые атрибуты, предшествующие src src= # начало атрибута src (?P ["'])? # необязательная открывающая кавычка (?P |