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".
Ниже представлена таблица соответствий :
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 имеет несколько методов и атрибутов .
Наиболее важные из них :
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 имеет несколько методов и атрибутов :
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( ... )
Опции компиляции
Некоторые опции компиляции :
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 :
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 | |
|