Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

Python Tutorial 5

1 Regular Expression

Стандартный модуль re добавлен , начиная с версии 1.5. Для более ранних версий работает модуль regex .

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

Вот полный список этих метасимволов :

 . ^ $ * + ? { [ ] \ | ( )
 

Рассмотрим комбинацию из квадратных скобок "[" и "]". Они используются для определения символьного класса-шаблона . Символы внутри такого класса можно задавать индивидуально или задать в диапазоне с разделителем "-". Например, [abc] будет соответствовать любому из символов "a", "b", или "c"; или что то же самое [a-c] . Если нужно задать нижний регистр , это будет [a-z].

Метасимволы не входят внутрь такого класса-шаблона. Хотя , если метасимвол входит в класс-шаблон , как в случае с [akm$] , то он будет соответствовать любому из символов "a", "k", "m", или "$" .

Для исключения какого-то символа из класса-шаблона нужно использовать "^" . Например , [^5] будет соответствовать любому символу , кроме "5".

Важным метасимволом является обратный слэш "\", который подчас может служить разделителем для различных последовательностей или началом какой-то символьной комбинации . Вот несколько таких комбинаций :

\d
десятичная цифра; эквивалентно классу [0-9].

\D
не-цифра; соответствует классу [^0-9].

\s
пробел; соответствует классу [ \t\n\r\f\v].

\S
не-пробел; соответствует [^ \t\n\r\f\v].

\w
буквенно-числовой символ; соответствует [a-zA-Z0-9_].

\W
не-буквенно-числовой символ; соответствует [^a-zA-Z0-9_].

Эти последовательности могут включаться в шаблон , например [\s,.] соответствует любому пробелу , или "," или ".".

И наконец последний метасимвол в этой серии - это точка .. Она соответствеут всему , окромя символу новой строки .

Другим важным метасимволом для определения повторов является звездочка * . Например , шаблон ca*t будет соответствовать "ct" (0 "a" символов), "cat" (1 "a"), "caaat" (3 "a" символов), и так далее.

В качестве примера рассмотрим шаблон a[bcd]*b и поисковую строку "abcbd". Ниже представлена таблица соответствий :

Step  Matched  Explanation 
1 a The a in the RE matches.
2 abcbd The engine matches [bcd]*, going as far as it can, which is to the end of the string.
3 Failure The engine tries to match b, but the current position is at the end of the string, so it fails.
4 abcb Back up, so that [bcd]* matches one less character.
5 Failure Try b again, but the current position is at the last character, which is a "d".
6 abc Back up again, so that [bcd]* is only matching "bc".
6 abcb Try b again. This time but the character at the current position is "b", so it succeeds.

Другим символом повтора является плюс +. Разница между * и + в том , что * соответствует нулю или больше раз, в то время как + требует хотя бы одного совпадения. Например , ca+t будет соответствовать "cat" (1 "a"), "caaat" (3 "a"'s), но не соответствует "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", в котором 4 слэша .

Кстати говоря , {0,} - то же самое , что и звезда *, {1,} - эквивалентно плюсу +, и {0,1} - то же самое , что и вопрос ?.

Компиляция регулярных выражений

Регулярные выражения компилируются в RegexObject , который имеет методы поиска шаблонов или выполнения строковых операций .

 >>> import re
 >>> p = re.compile('ab*')
 >>> print p
 <re.RegexObject instance at 80b4150>
 

re-модуль - модуль , написанный на С и включенный в питон аналогично модулям socket or zlib.

RegexObject имеет несколько методов и атрибутов . Наиболее важные из них :

Method/Attribute  Назначение 
match() Соответствие шаблону с начала строки
search() Соответствие шаблону с любой позиции строки
findall() Нахождение всех соответствий , возвращает список
finditer() Нахождение всех соответствий , возвращает итератор

match() и search() возвращают None , если соответствие не найдено . В противном случае возвращается инстанс MatchObject , включающий информацию о соответствии: позицию , подстроку, и т.д.

На http://kodos.sourceforge.net можно найти тулзу для интерактивной отладки .

Пример:

 Python 2.2.2 (#1, Feb 10 2003, 12:57:01)
 >>> import re
 >>> p = re.compile('[a-z]+')
 >>> p
 <_sre.SRE_Pattern object at 80c3c28>
 

Теперь попробуем найти соответствие для пустой строки - результат None :

 >>> p.match("")
 >>> print p.match("")
 None
 

Теперь получим положительный результат и сохраним его в переменной :

 >>> m = p.match( 'tempo')
 >>> print m
 <_sre.SRE_Match object at 80c4f68>
 

Инстанс MatchObject имеет несколько методов и атрибутов :

Method/Attribute  Назначение 
group() Возвращает строку , соответствующую шаблону
start() Возвращает стартовую позицию совпадения
end() Возвращает конечную позицию совпадения
span() Возвращает пару позиций

Попробуем:

 >>> m.group()
 'tempo'
 >>> m.start(), m.end()
 (0, 5)
 >>> m.span()
 (0, 5)
 

 >>> print p.match('::: message')
 None
 >>> m = p.search('::: message') ; print m
 <re.MatchObject instance at 80c9650>
 >>> m.group()
 'message'
 >>> m.span()
 (4, 11)
 

В программах обычно инстанс MatchObject сохраняют в переменной , после чего проверяют :

 p = re.compile( ... )
 m = p.match( 'string goes here' )
 if m:
     print 'Match found: ', m.group()
 else:
     print 'No match'
 

findall() возвращает список совпадений :

 >>> p = re.compile('\d+')
 >>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
 ['12', '11', '10']
 

finditer() возвращает последовательность MatchObject-инстансов как итератор :

 >>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')
 >>> iterator
 <callable-iterator object at 0x401833ac>
 >>> for match in iterator:
 ...     print match.span()
 ...
 (0, 2)
 (22, 24)
 (29, 31)
 

Вообще нет особой необходимости создавать инстанс RegexObject , для этого модуль re имеет набор высокоуровневых функций match(), search(), sub(), и т.д.

 >>> print re.match(r'From\s+', 'Fromage amk')
 None
 >>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998')
 <re.MatchObject instance at 80c5978>
 

Использование инстанса RegexObject может стать актуальным , если регулярные выражения используются очень часто . В этом случае компиляция может быть собрана в одном месте :

 ref = re.compile( ... )
 entityref = re.compile( ... )
 charref = re.compile( ... )
 starttagopen = re.compile( ... )
 

Опции компиляции

Некоторые опции компиляции :

Flag  Значение 
DOTALL, S Соответствие любому символу , включая символ новой строки
IGNORECASE, I Делает различимыми регистры
LOCALE, L Локальное соответствие
MULTILINE, M Многострочное соответствие
VERBOSE, X Детализация поискового шаблона

IGNORECASE
Например, [A-Z] при таком флаге будет соответствовать и Spam , и "spam", и "spAM".

LOCALE
Позволяет работать с различными языковыми локалями.

VERBOSE
Позволяет написать удобочитаемые реглярные выражения с комментариями . Например :

 charref = re.compile(r"""
  &[#]		     # Start of a numeric entity reference
  (
    [0-9]+[^0-9]      # Decimal form
    | 0[0-7]+[^0-7]   # Octal form
    | x[0-9a-fA-F]+[^0-9a-fA-F] # Hexadecimal form
  )
 """, re.VERBOSE)
 

Без verbose , RE выглядело бы так :

 charref = re.compile("&#([0-9]+[^0-9]"
                      "|0[0-7]+[^0-7]"
                      "|x[0-9a-fA-F]+[^0-9a-fA-F])")
 

Еще несколько метасимволов

|
Символ вертикальный слэш выполняет роль оператора ``or'' . Если A и B - регулярные выражения, A|B будет соответствовать любой строке , соответствующей "A" или "B". Crow|Servo будет соответствовать "Crow" или "Servo", но не "Cro", "w" , "S", или "ervo".

^
Соответствует началу строки :

 >>> print re.search('^From', 'From Here to Eternity')
 <re.MatchObject instance at 80c1520>
 >>> print re.search('^From', 'Reciting From Memory')
 None
 

$
Соответствует концу строки :

 >>> print re.search('}$', '{block}')
 <re.MatchObject instance at 80adfa8>
 >>> print re.search('}$', '{block} ')
 None
 >>> print re.search('}$', '{block}\n')
 <re.MatchObject instance at 80adfa8>
 

\b
Разделитель слов .

Cледующий пример соответствует "class" для слова целиком и не соответствует , если находится внутри другого слова :

 >>> p = re.compile(r'\bclass\b')
 >>> print p.search('no class at all')
 <re.MatchObject instance at 80c8f28>
 >>> print p.search('the declassified algorithm')
 None
 >>> print p.search('one subclass is')
 None
 

Или так :

 >>> p = re.compile('\bclass\b')
 >>> print p.search('no class at all')
 None
 >>> print p.search('\b' + 'class' + '\b')
 <re.MatchObject instance at 80c3ee0>
 

Группировка

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

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

 >>> p = re.compile('(a)b')
 >>> m = p.match('ab')
 >>> m.group()
 'ab'
 >>> m.group(0)
 'ab'
 

Группировка может быть вложенной :

 >>> p = re.compile('(a(b)c)d')
 >>> m = p.match('abcd')
 >>> m.group(0)
 'abcd'
 >>> m.group(1)
 'abc'
 >>> m.group(2)
 'b'
 

group() может иметь в качестве параметров группу чисел и вернуть массив :

 >>> m.group(2,1,2)
 ('b', 'abc', 'b')
 

groups() возвращает массив строк для всех подгрупп .

 >>> m.groups()
 ('abc', 'b')
 

В следующем примере находятся двойные слова в строке :

 >>> p = re.compile(r'(\b\w+)\s+\1')
 >>> p.search('Paris in the the spring').group()
 'the the'
 

Следующий шаблон находит файлы , имена которых имеют расширение .bat или .exe :
.*[.](?!bat$|exe$).*$

Для модификации поисковых строк используются методы обьекта RegexObject :

Method/Attribute  Назначение 
split() Разбиение строки на список
sub() Поиск и замена подстрок
subn() То же , что и sub(), но возвращает новую строку

Примеры работы split :

 >>> p = re.compile(r'\W+')
 >>> p.split('This is a test, short and sweet, of split().')
 ['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']
 >>> p.split('This is a test, short and sweet, of split().', 3)
 ['This', 'is', 'a', 'test, short and sweet, of split().']
 

Другой пример :

 >>> p = re.compile(r'\W+')
 >>> p2 = re.compile(r'(\W+)')
 >>> p.split('This... is a test.')
 ['This', 'is', 'a', 'test', '']
 >>> p2.split('This... is a test.')
 ['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']
 

Другая версия :

 >>> re.split('[\W]+', 'Words, words, words.')
 ['Words', 'words', 'words', '']
 >>> re.split('([\W]+)', 'Words, words, words.')
 ['Words', ', ', 'words', ', ', 'words', '.', '']
 >>> re.split('[\W]+', 'Words, words, words.', 1)
 ['Words', 'words, words.']
 

Search and Replace

Поиск и замена выполняются с помощью встроенной функции sub() .

В следующем примере цвет заменяется на colour столько раз , на сколько указывает 2-й аргумент :

 >>> p = re.compile( '(blue|white|red)')
 >>> p.sub( 'colour', 'blue socks and red shoes')
 'colour socks and colour shoes'
 >>> p.sub( 'colour', 'blue socks and red shoes', count=1)
 'colour socks and red shoes'
 

Пример для subn() :

 >>> p = re.compile( '(blue|white|red)')
 >>> p.subn( 'colour', 'blue socks and red shoes')
 ('colour socks and colour shoes', 2)
 >>> p.subn( 'colour', 'no colours at all')
 ('no colours at all', 0)
 

В следующем примере "section" меняется на "subsection":

 >>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)
 >>> p.sub(r'subsection{\1}','section{First} section{second}')
 'subsection{First} subsection{second}'
 

Замещаемое выражение может быть функцией :

 >>> def hexrepl( match ):
 ...     "Return the hex string for a decimal number"
 ...     value = int( match.group() )
 ...     return hex(value)
 ...
 >>> p = re.compile(r'\d+')
 >>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')
 'Call 0xffd2 for printing, 0xc000 for user code.'
 
Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье
Юрий
  Здесь написано: 

findall() возвращает список совпадений :

 >>> p = re.compile('d+')
 >>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
 ['12', '11', '10']
 

А если так:

p = re.compile('d(d)')
>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
['2', '1', '0']

findall() возвращает только группы. А как все-таки получить все совпадения ?
2007-07-23 18:57:09