Х
Хауди хо
Guest
Регулярные выражения (РВ) это, по существу, крошечный язык программирования, встроенный в Python и доступный при помощи модуля re. Используя его, вы указывается правила для множества возможных строк, которые вы хотите проверить; это множество может содержать английские фразы, или адреса электронной почты, или TeX команды, или все что угодно. С помощью РВ вы можете задавать вопросы, такие как «Соответствует ли эта строка шаблону?», или «Совпадает ли шаблон где-нибудь с этой строкой?». Вы можете также использовать регулярные выражения, чтобы изменить строку или разбить ее на части различными способами.
Шаблоны регулярных выражений компилируются в серии байт-кода, которые затем исполняются соответствующим движком написанным на C. Для продвинутого использования может быть важно уделять внимание тому, как движок будет выполнять данное регулярное выражение, и писать его так, чтобы получался байт-код, который работает быстрее. Оптимизация не рассматривается в этом документе, так как она требует от вас хорошего понимания внутренних деталей движка.
Язык регулярных выражений относительно мал и ограничен, поэтому не все возможные задачи по обработке строк можно сделать с помощью регулярных выражений. Также существуют задачи, которые можно сделать с помощью регулярных выражений, но выражения оказываются слишком сложными. В этих случаях может быть лучше написать обычный Python код, пусть он будет работать медленнее, чем разработанное регулярное выражение, но будет более понятен.
Простые шаблоны
Мы начнем с изучения простейших регулярных выражений. Поскольку регулярные выражения используются для работы со строками, мы начнем с наиболее распространенной задачи — соответствия символов.
За подробным объяснением технической стороны регулярных выражений (детерминированных и недетерминированных конечных автоматов) вы можете обратиться к практически любому учебнику по написанию компиляторов.
Соответствие символов
Большинство букв и символов соответствуют сами себе. Например, регулярное выражение test будет в точности соответствовать строке test (Вы можете включить режим без учета регистра, что позволит этому регулярному выражению также соответствовать Test или TEST, но об этом позже).
Из этого правила есть исключения; некоторые символы это специальные метасимволы, и сами себе не соответствуют. Вместо этого они указывают, что должна быть найдена некоторая необычная вещь, или влияют на другие части регулярного выражения, повторяя или изменяя их значение. Большая часть этого пособия посвящена обсуждению различных метасимволов и тому, что они делают.
Вот полный список метасимволов; их значения будут обсуждаться в остальной части этого HOWTO.
. ^ $ * + ? { [ ] \ | ( )
Первые метасимволы, что мы рассмотрим это [ и ]. Они используются для определения класса символов, являющегося набором символов, с которыми вы ищите совпадение. Символы могут быть перечислены по отдельности, или в виде некоторого диапазона символов, обозначенного первым и последним символом, разделенных знаком '-'. Например, [abc] будет соответствовать любому из символов a, b или c; это то же самое, что выражение [a-c], использующее диапазон для задания того же множества символов. Если вы хотите сопоставить только строчные буквы, РВ будет иметь вид [a-z].
Метасимволы не активны внутри классов. Например, [akm$] будет соответствовать любому из символов 'a', 'k', 'm' или '$'. Знак '$' это обычно метасимвол (как видно из списка символов выше), но внутри класса символов он лишается своей особой природы.
Для того, чтобы находить соответствие символам вне этого класса, в начале класса добавляется символ '^'. Например, выражение [^5] соответствует любому символу, кроме '5'.
Пожалуй, наиболее важным является метасимвол обратной косой черты \. Как и в строковых литералах Python, за бэкслешем могут следовать различные символы, обозначающие разные специальные последовательности. Он также используется для экранирования метасимволов, чтобы их можно было использовать в шаблонах; например, если нужно найти соответствие [ или \, для того чтобы лишить их своей особой роли метасимволов, перед ним нужно поставить обратную косую черту: \[ или \\.
Некоторые из специальных последовательностей, начинающихся с '\' представляют предопределенные наборы символов, часто бывающие полезными, такие как набор цифр, набор букв, или множества всего, что не является пробелами, символами табуляции и т. д. (whitespace). Следующие предопределенные последовательности являются их подмножеством. Полный список последовательностей и расширенных определений классов для Юникод-строк смотрите в последней части Regular Expression Syntax.
\d
Соответствует любой цифре; эквивалент класса [0-9].
\D
Соответствует любому нечисловому символу; эквивалент класса [^0-9].
\s
Соответствует любому символу whitespace; эквивалент [ \t\n\r\f\v].
\S
Соответствует любому не-whitespace символу; эквивалент [^ \t\n\r\f\v].
\w
Соответствует любой букве или цифре; эквивалент [a-zA-Z0-9_].
\W
Наоборот; эквивалент [^a-zA-Z0-9_].
Эти последовательности могут быть включены в класс символов. Например, [\s,.] является характер класс, который будет соответствовать любому whitespace-символу или запятой или точке.
Последний метасимвол в этом разделе это '.'. Он соответствует всем символам, кроме символа новой строки, но есть альтернативный режим (re.DOTALL), где это множество будет включать и его. '.' часто используется там, где вы хотите сопоставить «любой символ».
Повторяющиеся вещи
Возможность сопоставлять различные наборы символов это первое, что регулярные выражения могут сделать и что не всегда можно сделать строковыми методами. Однако, если бы это было единственной дополнительной возможностью, они бы не были так интересны. Другая возможность заключается в том, что вы можете указать какое число раз должна повторяться часть регулярного выражения.
Первый метасимвол для повторения это *. Он указывает, что предыдущий символ может быть сопоставлен ноль и более раз, вместо одного сравнения.
Например, ca*t будет соответствовать ct (0 символов a), cat (1 символ a), caaat (3 символа a), и так далее. Движок регулярных выражений имеет различные внутренние ограничения вытекающие из размера int типа для C, что не позволяет проводить ему сопоставление более 2 миллиардов символов 'a'. (Надеюсь, вам это не понадобится).
Повторения, такие как * называют жадными (greedy); движок будет пытаться повторить его столько раз, сколько это возможно. Если следующие части шаблона не соответствуют, движок вернется назад и попытается попробовать снова с несколькими повторами символа.
Пошаговое рассмотрение какого-нибудь примера сделает объяснение более ясным. Давайте рассмотрим выражение a[bcd]*b. Оно соответствует букве 'a', нулю или более символов из класса [bcd], и наконец, заключительной букве 'b'. Теперь представим себе сопоставление этого регулярного выражения строке abcbd. Вот как происходит сравнение поэтапно:
1. a — 'a' соответствует регулярному выражению
2. abcbd — движок сопоставляет [bcd]* на как можно большем числе символов, то есть до конца строки (поскольку все символы соответствуют классу в скобках [])
3. Провал — движок пытается сопоставить последний символ в регулярном выражении — букву b, но текущая позиция уже в конце строки, где нет никаких символов, так что он терпит неудачу.
4. abcb — вернулись назад, уменьшили на один символ сопоставление с [bcd]*
5. Провал — пытаемся снова найти b, но в конце только d
6. abc — снова возвращаемся назад, теперь [bcd]* это только bc
7. abcb — снова ищем последний символ регулярного выражения — b. Теперь он действительно находится на нужной позиции и мы добиваемся успеха
Итак, был достигнут конец РВ и сопоставление с ним дало abcb. Этот пример показал, как движок сначала забирается так далеко, как может, и, если не находит соответствия, возвращается назад, снова и снова работая с остатком регулярного выражения. Он будет делать так до тех пор, пока не получит ноль совпадений для [bcd]*, и, если и тогда не получится совпадения, то заключит, что строка совсем не соответствует шаблону РВ.
Другой метасимвол повторения это +, повторяющий последовательность сравнения один или более раз. Обратите особое внимание на разницу между * и +. * требует соответствия необходимой части ноль или более раз, то есть повторяемое может и не присутствовать вовсе, а + требует, по крайней мере одно вхождение. Для аналогичного примера ca+t будет сопоставляться cat или, например, caaat, но никак не ct.
Есть еще два повторяющих спецификатора. Знак вопроса, ?, проверяющий наличие совпадения ноль или один раз. Например, home-?brew соответствует как homebrew, так и home-brew.
Наиболее полный повторяющий спецификатор это {m,n}, где m и n — целые числа. Этот определитель означает, что здесь должно быть не менее m и не более n повторений. Например, a/{1,3}b соответствует a/b, a//b и a///b. Это не может быть ab, строка в которой нет слэшей или a////b, в которой их четыре.
Вы можете не задавать m или n, тогда для отсутствующего предполагается наиболее разумное значение. Опускание m означает, что нижний предел 0, опускание n предполагает верхним пределом бесконечность, но, как уже говорилось выше, последний ограничен памятью.
Читатели уже могли заметить, что все три остальных спецификатора могут быть выражены через последний. {0,} это то же, что *, {1,} эквивалентно +, и {0,1} может заменять знак ?.
Использование регулярных выражений
Теперь, когда мы рассмотрели несколько простых регулярных выражений, как мы можем использовать их в Python? Модуль re предоставляет интерфейс для регулярных выражений, что позволяет компилировать регулярные выражения в объекты, а затем выполнять с ними сопоставления.
Шаблоны регулярных выражений компилируются в серии байт-кода, которые затем исполняются соответствующим движком написанным на C. Для продвинутого использования может быть важно уделять внимание тому, как движок будет выполнять данное регулярное выражение, и писать его так, чтобы получался байт-код, который работает быстрее. Оптимизация не рассматривается в этом документе, так как она требует от вас хорошего понимания внутренних деталей движка.
Язык регулярных выражений относительно мал и ограничен, поэтому не все возможные задачи по обработке строк можно сделать с помощью регулярных выражений. Также существуют задачи, которые можно сделать с помощью регулярных выражений, но выражения оказываются слишком сложными. В этих случаях может быть лучше написать обычный Python код, пусть он будет работать медленнее, чем разработанное регулярное выражение, но будет более понятен.
Простые шаблоны
Мы начнем с изучения простейших регулярных выражений. Поскольку регулярные выражения используются для работы со строками, мы начнем с наиболее распространенной задачи — соответствия символов.
За подробным объяснением технической стороны регулярных выражений (детерминированных и недетерминированных конечных автоматов) вы можете обратиться к практически любому учебнику по написанию компиляторов.
Соответствие символов
Большинство букв и символов соответствуют сами себе. Например, регулярное выражение test будет в точности соответствовать строке test (Вы можете включить режим без учета регистра, что позволит этому регулярному выражению также соответствовать Test или TEST, но об этом позже).
Из этого правила есть исключения; некоторые символы это специальные метасимволы, и сами себе не соответствуют. Вместо этого они указывают, что должна быть найдена некоторая необычная вещь, или влияют на другие части регулярного выражения, повторяя или изменяя их значение. Большая часть этого пособия посвящена обсуждению различных метасимволов и тому, что они делают.
Вот полный список метасимволов; их значения будут обсуждаться в остальной части этого HOWTO.
. ^ $ * + ? { [ ] \ | ( )
Первые метасимволы, что мы рассмотрим это [ и ]. Они используются для определения класса символов, являющегося набором символов, с которыми вы ищите совпадение. Символы могут быть перечислены по отдельности, или в виде некоторого диапазона символов, обозначенного первым и последним символом, разделенных знаком '-'. Например, [abc] будет соответствовать любому из символов a, b или c; это то же самое, что выражение [a-c], использующее диапазон для задания того же множества символов. Если вы хотите сопоставить только строчные буквы, РВ будет иметь вид [a-z].
Метасимволы не активны внутри классов. Например, [akm$] будет соответствовать любому из символов 'a', 'k', 'm' или '$'. Знак '$' это обычно метасимвол (как видно из списка символов выше), но внутри класса символов он лишается своей особой природы.
Для того, чтобы находить соответствие символам вне этого класса, в начале класса добавляется символ '^'. Например, выражение [^5] соответствует любому символу, кроме '5'.
Пожалуй, наиболее важным является метасимвол обратной косой черты \. Как и в строковых литералах Python, за бэкслешем могут следовать различные символы, обозначающие разные специальные последовательности. Он также используется для экранирования метасимволов, чтобы их можно было использовать в шаблонах; например, если нужно найти соответствие [ или \, для того чтобы лишить их своей особой роли метасимволов, перед ним нужно поставить обратную косую черту: \[ или \\.
Некоторые из специальных последовательностей, начинающихся с '\' представляют предопределенные наборы символов, часто бывающие полезными, такие как набор цифр, набор букв, или множества всего, что не является пробелами, символами табуляции и т. д. (whitespace). Следующие предопределенные последовательности являются их подмножеством. Полный список последовательностей и расширенных определений классов для Юникод-строк смотрите в последней части Regular Expression Syntax.
\d
Соответствует любой цифре; эквивалент класса [0-9].
\D
Соответствует любому нечисловому символу; эквивалент класса [^0-9].
\s
Соответствует любому символу whitespace; эквивалент [ \t\n\r\f\v].
\S
Соответствует любому не-whitespace символу; эквивалент [^ \t\n\r\f\v].
\w
Соответствует любой букве или цифре; эквивалент [a-zA-Z0-9_].
\W
Наоборот; эквивалент [^a-zA-Z0-9_].
Эти последовательности могут быть включены в класс символов. Например, [\s,.] является характер класс, который будет соответствовать любому whitespace-символу или запятой или точке.
Последний метасимвол в этом разделе это '.'. Он соответствует всем символам, кроме символа новой строки, но есть альтернативный режим (re.DOTALL), где это множество будет включать и его. '.' часто используется там, где вы хотите сопоставить «любой символ».
Повторяющиеся вещи
Возможность сопоставлять различные наборы символов это первое, что регулярные выражения могут сделать и что не всегда можно сделать строковыми методами. Однако, если бы это было единственной дополнительной возможностью, они бы не были так интересны. Другая возможность заключается в том, что вы можете указать какое число раз должна повторяться часть регулярного выражения.
Первый метасимвол для повторения это *. Он указывает, что предыдущий символ может быть сопоставлен ноль и более раз, вместо одного сравнения.
Например, ca*t будет соответствовать ct (0 символов a), cat (1 символ a), caaat (3 символа a), и так далее. Движок регулярных выражений имеет различные внутренние ограничения вытекающие из размера int типа для C, что не позволяет проводить ему сопоставление более 2 миллиардов символов 'a'. (Надеюсь, вам это не понадобится).
Повторения, такие как * называют жадными (greedy); движок будет пытаться повторить его столько раз, сколько это возможно. Если следующие части шаблона не соответствуют, движок вернется назад и попытается попробовать снова с несколькими повторами символа.
Пошаговое рассмотрение какого-нибудь примера сделает объяснение более ясным. Давайте рассмотрим выражение a[bcd]*b. Оно соответствует букве 'a', нулю или более символов из класса [bcd], и наконец, заключительной букве 'b'. Теперь представим себе сопоставление этого регулярного выражения строке abcbd. Вот как происходит сравнение поэтапно:
1. a — 'a' соответствует регулярному выражению
2. abcbd — движок сопоставляет [bcd]* на как можно большем числе символов, то есть до конца строки (поскольку все символы соответствуют классу в скобках [])
3. Провал — движок пытается сопоставить последний символ в регулярном выражении — букву b, но текущая позиция уже в конце строки, где нет никаких символов, так что он терпит неудачу.
4. abcb — вернулись назад, уменьшили на один символ сопоставление с [bcd]*
5. Провал — пытаемся снова найти b, но в конце только d
6. abc — снова возвращаемся назад, теперь [bcd]* это только bc
7. abcb — снова ищем последний символ регулярного выражения — b. Теперь он действительно находится на нужной позиции и мы добиваемся успеха
Итак, был достигнут конец РВ и сопоставление с ним дало abcb. Этот пример показал, как движок сначала забирается так далеко, как может, и, если не находит соответствия, возвращается назад, снова и снова работая с остатком регулярного выражения. Он будет делать так до тех пор, пока не получит ноль совпадений для [bcd]*, и, если и тогда не получится совпадения, то заключит, что строка совсем не соответствует шаблону РВ.
Другой метасимвол повторения это +, повторяющий последовательность сравнения один или более раз. Обратите особое внимание на разницу между * и +. * требует соответствия необходимой части ноль или более раз, то есть повторяемое может и не присутствовать вовсе, а + требует, по крайней мере одно вхождение. Для аналогичного примера ca+t будет сопоставляться cat или, например, caaat, но никак не ct.
Есть еще два повторяющих спецификатора. Знак вопроса, ?, проверяющий наличие совпадения ноль или один раз. Например, home-?brew соответствует как homebrew, так и home-brew.
Наиболее полный повторяющий спецификатор это {m,n}, где m и n — целые числа. Этот определитель означает, что здесь должно быть не менее m и не более n повторений. Например, a/{1,3}b соответствует a/b, a//b и a///b. Это не может быть ab, строка в которой нет слэшей или a////b, в которой их четыре.
Вы можете не задавать m или n, тогда для отсутствующего предполагается наиболее разумное значение. Опускание m означает, что нижний предел 0, опускание n предполагает верхним пределом бесконечность, но, как уже говорилось выше, последний ограничен памятью.
Читатели уже могли заметить, что все три остальных спецификатора могут быть выражены через последний. {0,} это то же, что *, {1,} эквивалентно +, и {0,1} может заменять знак ?.
Использование регулярных выражений
Теперь, когда мы рассмотрели несколько простых регулярных выражений, как мы можем использовать их в Python? Модуль re предоставляет интерфейс для регулярных выражений, что позволяет компилировать регулярные выражения в объекты, а затем выполнять с ними сопоставления.