Поиск по блогу

вторник, 13 января 2015 г.

Regular Expressions Python ... и мой перечень главных команд

Конспект первой части доументации Python по теме "Регулярные выражения" есть перевод на Хабре. На вторую часть терпения не хватило, но надо помнить, что я прочитал HowTo (а есть еще аполный справочник), и то, что я не включил сюда такие мощные функции, как "Splitting Strings", "Search and Replace"

Метасимволы

In []:
.        # any char, except /n 
^        # beginning of row 
$        # end of row
*        #{0,}
+        #{1,}
?        #{0,1}
{ }      #{min,max} 
[ ]      # - class  
\        #  \w - Соответствует любой букве или цифре; эквивалент [a-zA-Z0-9_], undo metasimbols
|        # OR 
In []:
*?       #non-greedy qualifiers *?, +?, ??, or {m,n}?

(Мета)Символы для продвинутых из второй части Regular Expression HOWTO

In []:
( )      # group and  *, +, ?, or {m,n}
(ab)*    #will match zero or more repetitions of ab
In []:
\A       #Matches only at the start of the string
\Z       #Matches only at the end of the string
\b       #Word boundary
\B       #this is the opposite of \b
In []:
(?=...)  #Positive lookahead assertion
(?!...)   #Negative lookahead assertion

Первые метасимволы, что мы рассмотрим это [ и ] - далее используется термин "класс" (символов ?!)

In []:
# Например, 
[abc] будет соответствовать любому из символов a, b или c; это то же самое, что выражение [a-c]

Метасимволы не активны внутри классов. Например:

In []:
[akm$] будет соответствовать любому из символов 'a', 'k', 'm' или '$'

Знак '$' это обычно метасимвол (как видно из списка символов выше), но внутри класса символов он лишается своей особой природы.

Для того, чтобы находить соответствие символам вне этого класса,

In []:
# в начале класса добавляется символ '^'. Например, выражение 
[^5] соответствует любому символу, кроме '5'.

Пожалуй, наиболее важным является метасимвол обратной косой черты \

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

In []:
\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_].

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

In []:
[\s,.] -  #в классе три символа: \s , .

является характер класс, который будет соответствовать любому whitespace-символу или запятой или точке

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

In []:
 [ или \, 
 #для того чтобы лишить их своей особой роли метасимволов, 
 #перед ним нужно поставить обратную косую черту: 
 \[ или \\

Метасимвол точка '.' часто используется там, где вы хотите сопоставить «любой символ»

Последний метасимвол в этом разделе это '.'. Он соответствует всем символам, кроме символа новой строки, но есть альтернативный режим (re.DOTALL), где это множество будет включать и его. '.' часто используется там, где вы хотите сопоставить «любой символ».

Вы можете указать какое число раз должна повторяться часть регулярного выражения

In []:
* - #предыдущий символ может быть сопоставлен ноль и более раз
In []:
#ca*t будет соответствовать 
ct       #(0 символов a), 
cat      #(1 символ a), 
caaat    #(3 символа a), 
caa...at #  ... и так далее

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

Повторения, такие как * называют жадными (greedy); движок будет пытаться повторить его столько раз, сколько это возможно.
Если следующие части шаблона не соответствуют, движок вернется назад и попытается попробовать снова с несколькими повторами символа.

Давайте рассмотрим выражение a[bcd]*b

In []:
#Оно соответствует 
#букве 'a', 
#нулю или более символов из класса [bcd], 
#и наконец, заключительной букве 'b'. 

Теперь представим себе сопоставление этого регулярного выражения строке abcbd

In []:
# регулярное выражение 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. 
#    Теперь он действительно находится на нужной позиции и мы добиваемся успеха

Другой метасимвол повторения это +, повторяющий последовательность сравнения один или более раз

In []:
#Обратите особое внимание на разницу между * и +. * требует соответствия необходимой части 
#ноль или более раз, то есть повторяемое может и не присутствовать вовсе, 
#а + требует, по крайней мере одно вхождение. 
#Для аналогичного примера ca+t будет сопоставляться cat или, например, caaat, но никак не ct

Знак вопроса, ?, проверяющий наличие совпадения ноль или один раз

In []:
#Например, 
home-?brew #соответствует как 
homebrew, #так и 
home-brew.

Наиболее полный повторяющий спецификатор это {m,n}, где m и n — целые числа

In []:
#Этот определитель означает, что здесь должно быть не менее m и не более n повторений. Например, 
a/{1,3}b #соответствует 
a/b, 
a//b 
a///b. 
#Это не может быть ab, строка в которой нет слэшей или a////b, в которой их четыре.

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

In []:
{0,} #это то же, что *, 
{1,} #эквивалентно +, и 
{0,1} #может заменять знак ?.

Регулярные выражения часто будут записываться с использованием сырых строк с префиксом 'r'

Решение заключается в использовании для регулярных выражений «сырых» строк (raw string); в строковых литералах с префиксом 'r' слэши никак не обрабатываются, так что
r"\n" это строка из двух символов
('\' и 'n'), а "\n" — из одного символа новой строки.
Поэтому регулярные выражения часто будут записываться с использованием сырых строк.

Регулярные выражения компилируются в объекты шаблонов,

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

In []:
>>> import re
>>> p = re.compile('ab*')
>>> print p
<_sre.SRE_Pattern object at 0x...>

Регулярное выражение передается re.compile() как строка. Регулярные выражения обрабатываются как строки, поскольку не являются частью языка Python, и нет никакого специального синтаксиса для их выражения.
... Вместо этого имеется модуль re, представляющий собой обертку модуля на С, подобно модулям socket или zlib.

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

In []:
#Метод/атрибут Цель
match()     #Определить, начинается ли совпадение регулярного выражения с начала строки
search() #Сканировать всю строку в поисках всех мест совпадений с регулярным выражением
findall() #Найти все подстроки совпадений с регулярным выражением и вернуть их в виде списка
finditer() #Найти все подстроки совпадений с регулярным выражением и вернуть их в виде итератора

I Логическое ИЛИ

|
Соответствует оператору ИЛИ. Если А и В являются регулярными выражениями, то A|B будет
соответствовать любая строка, которая соответствует А или В.
Метасимвол | имеет очень низкий приоритет для того, чтобы заставить его работать разумно, когда вы
чередуете несколько символов строки.
Crow|Servo будет искать соответствие
либо Crow, либо Servo,
а не Cro('w' или 'S')ervo.

^ Ищет соответствие только в начале строки

Если включен флаг MULTILINE, как говорилось в прошлой части, то происходит сравнение и для каждой части после символа новой строки.

Например, если вы хотите найти только те строки, у которых в начале имеется From, то в регулярном выражении записывается ^From:

In []:
>>> print re.search('^From', 'From Here to Eternity')
<_sre.SRE_Match object at 0x...>
>>> print re.search('^From', 'Reciting From Memory')
None

$ Ищет соответствие только в конце строки

То же, что ^, но в конце строки, которая определяется либо, собственно по концу строки как таковому, либо по символу новой строки

In []:
>>> print re.search('}$', '{block}')
<_sre.SRE_Match object at 0x...>
>>> print re.search('}$', '{block} ')
None
>>> print re.search('}$', '{block}\n')
<_sre.SRE_Match object at 0x...>
In []:
\A #- Совпадение только в начале строки, то есть тоже, что ^, но не зависит от флага MULTILINE

\Z #- Совпадение только в конце строки, то есть тоже, что $, но не зависит от флага MULTILINE

\b #- Граница слова. Слово определяется как последовательность символов чисел и/или букв, 
   #  так что границы слова представляют пробелы или любые символы, не относящиеся к перечисленным.

Следующий пример ищет слово class только когда это отдельное слово. Если оно содержится внутри другого слова, соответствия не находится:

In []:
>>> p = re.compile(r'\bclass\b')
>>> print p.search('no class at all')
<_sre.SRE_Match object at 0x...>
>>> print p.search('the declassified algorithm')
None
>>> print p.search('one subclass is')
None

Есть две тонкости, которые вы должны помнить при использовании этой специальной последовательности. Во-первых, это одно из худших столкновений между строковыми литералами Python и последовательностями регулярных выражений:
в строковых литералах Python, \b это символ backspace, ASCII значение 8. Если не использовать «сырые» строки, Python будет конвертировать \b в backspace, и ваше регулярное выражение будет не таким, как задумано:

In []:
>>> p = re.compile('\bclass\b')
>>> print p.search('no class at all')
None
>>> print p.search('\b' + 'class' + '\b')
<_sre.SRE_Match object at 0x...>

Во-вторых, внутри класса символов нельзя использовать данное сочетание, потому как сочетание \b для совместимости со строковыми литералами Python представляет символ backspace.

\B - Противоположное предыдущему сочетание, соответствующая текущей позиции не на границе слова.

Группировка

Регулярные выражения часто используются для разрезания строк написанием регулярных выражений, разделенных на несколько подгрупп, которые соответствуют различным компонентам запроса. Например, в стандарте RFC-822 в заголовке имеются различные поля, разделенные двоеточием:

In []:
From: author@example.com User-Agent: Thunderbird 1.5.0.9 (X11/20061227) MIME-Version: 1.0 To: editor@example.com

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

Группы обозначаются метасимволами в виде круглых скобок '(', ')'. '(' и ')' имеют такой же смысл, как в математических выражениях; они группируют вместе выражения, содержащиеся в них, и вы можете повторять содержание группы повторяющими квалификаторами, такими как , +, ? и {m, n}. Например, (ab) будет соответствовать нулю или более повторений ab.

In []:
>>> p = re.compile('(ab)*')
>>> print p.match('ababababab').span()
(0, 10)

Lookahead Assertions

In []:
#For example, in news.rc, news is the base name, and rc is the filename’s extension.

#The pattern to match this is quite simple:

.*[.].*$

#Notice that the . needs to be treated specially because it’s a metacharacter; I’ve put it inside a character class. 
#Also notice the trailing $; this is added to ensure that all the rest of the string must be included in the extension. 
#This regular expression matches foo.bar and autoexec.bat and sendmail.cf and printers.conf.

#Now, consider complicating the problem a bit; what if you want to match filenames where the extension is not bat? 
#Some incorrect attempts:

.*[.][^b].*$ 
#The first attempt above tries to exclude bat by requiring that the first character of the extension is not a b. 
#This is wrong, because the pattern also doesn’t match foo.bar.

.*[.]([^b]..|.[^a].|..[^t])$

#The expression gets messier when you try to patch up the first solution by requiring one of the following cases to match: 
#the first character of the extension isn’t b; the second character isn’t a; or the third character isn’t t. 
#This accepts foo.bar and rejects autoexec.bat, but it requires a three-letter extension and won’t accept a filename 
#with a two-letter extension such as sendmail.cf. We’ll complicate the pattern again in an effort to fix it.

.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$

#In the third attempt, the second and third letters are all made optional in order to allow matching extensions shorter 
#than three characters, such as sendmail.cf.

#The pattern’s getting really complicated now, which makes it hard to read and understand. 
#Worse, if the problem changes and you want to exclude both bat and exe as extensions, 
#the pattern would get even more complicated and confusing.

#A negative lookahead cuts through all this confusion:

.*[.](?!bat$).*$ 
#The negative lookahead means: if the expression bat doesn’t match at this point, try the rest of the pattern; if bat$ does match, 
#the whole pattern will fail. The trailing $ is required to ensure that something like sample.batch, 
#where the extension only starts with bat, will be allowed.

#Excluding another filename extension is now easy; simply add it as an alternative inside the assertion. 
#The following pattern excludes filenames that end in either bat or exe:

.*[.](?!bat$|exe$).*$


Посты чуть ниже также могут вас заинтересовать

Комментариев нет:

Отправить комментарий