За да извършваме обработка на регулярни изрази в Python, използваме модула re от стандартната библиотека. Той ви позволява да извличате, заменяте и разделяте низове, като използвате шаблони за регулярни изрази.
- re — Regular expression operations — Python 3.10.0 Documentation
- Regular Expression HOWTO — Python 3.10.0 Documentation
В този раздел първо ще обясним функциите и методите на модула re.
- Съставяне на шаблони на регулярни изрази:
compile()
- обект на съвпадение
- Проверете дали началото на символния низ съвпада, извлечете:
match()
- Проверете за съвпадения, които не се ограничават само до началото:
search()
- Проверете дали целият низ съвпада:
fullmatch()
- Получете списък на всички съответстващи части:
findall()
- Получаване на всички съвпадащи части като итератор:
finditer()
- Заменете съответстващата част:
sub()
,subn()
- Разделяне на низове с шаблони на регулярни изрази:
split()
След това ще обясня метасимволите (специални символи) и специалните последователности на регулярните изрази, които могат да се използват в модула re. В общи линии това е стандартният синтаксис на регулярните изрази, но внимавайте за задаването на флагове (особено за re.ASCII).
- Метасимволи на регулярни изрази, специални последователности и предупреждения в Python
- Задаване на флага
- Ограничено до ASCII символи:
re.ASCII
- Не се различават големи и малки букви:
re.IGNORECASE
- Подберете началото и края на всеки ред:
re.MULTILINE
- Задаване на няколко флага
- Ограничено до ASCII символи:
- Жадни и нежадни съвпадения
- Компилиране на шаблона на регулярния израз: compile()
- обект на съвпадение
- Проверявайте дали началото на даден низ съвпада, извлечете: match()
- Проверка за съвпадения, които не се ограничават само до началото, извличане: search()
- Проверка за съответствие на целия низ: fullmatch()
- Получаване на списък с всички съвпадащи части: findall()
- Получаване на всички съвпадащи части като итератор: finditer()
- Заменете съответстващите части: sub(), subn()
- Разделяне на низове с шаблони на регулярни изрази: split()
- Метасимволи на регулярни изрази, специални последователности и предупреждения в Python
- Задаване на флага
- Жадни и нежадни съвпадения
Компилиране на шаблона на регулярния израз: compile()
Има два начина за обработка на регулярни изрази в модула re.
Работете с функция
Първата е функция.re.match()
,re.sub()
Подобни функции са на разположение за извършване на извличане, заместване и други процеси с помощта на шаблони на регулярни изрази.
Подробностите за функциите ще бъдат описани по-късно, но при всички тях първият аргумент е низът на шаблона на регулярния израз, следван от низ, който трябва да бъде обработен, и т.н. Например в re.sub(), която извършва заместване, вторият аргумент е низът на заместването, а третият аргумент е низът, който трябва да се обработи.
import re
s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'
m = re.match(r'([a-z]+)@([a-z]+)\.com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
result = re.sub(r'([a-z]+)@([a-z]+)\.com', 'new-address', s)
print(result)
# new-address, new-address, ccc@zzz.net
Обърнете внимание, че [a-z] в шаблона на регулярния израз в този пример означава всеки символ от a до z (т.е. малка азбука), а + означава повторение на предишния шаблон (в този случай [a-z]) един или повече пъти. [a-z]+ съвпада с всеки низ, който повтаря един или повече знаци от малката азбука.
. е метасимвол (символ със специално значение) и трябва да се избягва с обратна наклонена черта.
Тъй като низовете с шаблони на регулярни изрази често използват много обратни наклонени черти, удобно е да се използват необработени низове, както в примера.
Изпълнява се в метод на обект с шаблон на регулярен израз
Вторият начин за обработка на регулярни изрази в модула re е обектният метод за регулярни изрази.
С помощта на re.compile() можете да компилирате низ от шаблони за регулярни изрази, за да създадете обект с шаблони за регулярни изрази.
p = re.compile(r'([a-z]+)@([a-z]+)\.com')
print(p)
# re.compile('([a-z]+)@([a-z]+)\\.com')
print(type(p))
# <class 're.Pattern'>
re.match()
,re.sub()
Например същият процес като тези функции може да се изпълни като методи match(),sub() на обекти с регулярни изрази.
m = p.match(s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
result = p.sub('new-address', s)
print(result)
# new-address, new-address, ccc@zzz.net
Всички функции re.xxx(), описани по-долу, се предоставят и като методи на обекта за регулярни изрази.
Ако повтаряте процес, при който се използва един и същ шаблон, по-ефективно е да генерирате обект на регулярен израз с re.compile() и да го използвате наоколо.
В следващия примерен код функцията се използва без компилиране за удобство, но ако искате да използвате един и същ шаблон многократно, се препоръчва да го компилирате предварително и да го изпълните като метод на обект с регулярни изрази.
обект на съвпадение
match(), search() и т.н. връщат обект на съвпадение.
s = 'aaa@xxx.com'
m = re.match(r'[a-z]+@[a-z]+\.[a-z]+', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
print(type(m))
# <class 're.Match'>
Съответстващият низ и позиция се получават чрез следните методи на обекта match.
- Получаване на местоположението на мача:
start()
,end()
,span()
- Получаване на съответстващия низ:
group()
- Получаване на символния низ за всяка група:
groups()
print(m.start())
# 0
print(m.end())
# 11
print(m.span())
# (0, 11)
print(m.group())
# aaa@xxx.com
Ако заградите част от шаблона на регулярен израз в низ със скоби(), частта ще бъде обработена като група. В този случай низът на частта, която съответства на всяка група в groups(), може да се получи като кортеж.
m = re.match(r'([a-z]+)@([a-z]+)\.([a-z]+)', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
print(m.groups())
# ('aaa', 'xxx', 'com')
Проверявайте дали началото на даден низ съвпада, извлечете: match()
match() връща обект match, ако началото на символния низ съвпада с шаблона.
Както беше споменато по-горе, обектът match може да се използва за извличане на съвпадащия подниз или просто за проверка дали е постигнато съвпадение.
match() ще провери само началото. Ако в началото няма съвпадащ низ, връща None.
s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'
m = re.match(r'[a-z]+@[a-z]+\.com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
m = re.match(r'[a-z]+@[a-z]+\.net', s)
print(m)
# None
Проверка за съвпадения, които не се ограничават само до началото, извличане: search()
Подобно на match() връща обект match, ако има съвпадение.
Ако има няколко съвпадащи части, ще бъде върната само първата съвпадаща част.
s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'
m = re.search(r'[a-z]+@[a-z]+\.net', s)
print(m)
# <re.Match object; span=(26, 37), match='ccc@zzz.net'>
m = re.search(r'[a-z]+@[a-z]+\.com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
Ако искате да получите всички съвпадащи части, използвайте findall() или finditer(), както е описано по-долу.
Проверка за съответствие на целия низ: fullmatch()
За да проверите дали целият низ отговаря на шаблона на регулярния израз, използвайте функцията fullmatch(). Това е полезно, например, за да проверите дали даден низ е валиден като имейл адрес или не.
Ако целият низ съвпада, се връща обект за съвпадение.
s = 'aaa@xxx.com'
m = re.fullmatch(r'[a-z]+@[a-z]+\.com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
Ако има несъвпадащи части (само частични съвпадения или никакви съвпадения), се връща None.
s = '!!!aaa@xxx.com!!!'
m = re.fullmatch(r'[a-z]+@[a-z]+\.com', s)
print(m)
# None
Функцията fullmatch() беше добавена в Python 3.4. Ако искате да направите същото в по-ранни версии, използвайте match() и съответстващ метасимвол $ в края. Ако целият низ от началото до края не съвпада, се връща None.
s = '!!!aaa@xxx.com!!!'
m = re.match(r'[a-z]+@[a-z]+\.com$', s)
print(m)
# None
Получаване на списък с всички съвпадащи части: findall()
функцията findall() връща списък с всички съвпадащи поднизове. Обърнете внимание, че елементите на списъка не са обекти на съвпадение, а низове.
s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'
result = re.findall(r'[a-z]+@[a-z]+\.[a-z]+', s)
print(result)
# ['aaa@xxx.com', 'bbb@yyy.com', 'ccc@zzz.net']
Броят на съвпадащите части може да се провери с помощта на вградената функция len(), която връща броя на елементите в списъка.
print(len(result))
# 3
Групирането със скоби() в шаблон на регулярен израз връща списък от кортежи, чиито елементи са низовете от всяка група. Това е еквивалентно на groups() в обекта match.
result = re.findall(r'([a-z]+)@([a-z]+)\.([a-z]+)', s)
print(result)
# [('aaa', 'xxx', 'com'), ('bbb', 'yyy', 'com'), ('ccc', 'zzz', 'net')]
Груповите скоби () могат да се вмъкват, така че ако искате да получите и цялото съвпадение, просто затворете цялото съвпадение в скоби ().
result = re.findall(r'(([a-z]+)@([a-z]+)\.([a-z]+))', s)
print(result)
# [('aaa@xxx.com', 'aaa', 'xxx', 'com'), ('bbb@yyy.com', 'bbb', 'yyy', 'com'), ('ccc@zzz.net', 'ccc', 'zzz', 'net')]
Ако не бъде намерено съвпадение, се връща празен кортеж.
result = re.findall('[0-9]+', s)
print(result)
# []
Получаване на всички съвпадащи части като итератор: finditer()
finditer() връща всички съвпадащи части като итератор. Елементите не са низове, както при findall(), а обекти на съвпадение, така че можете да получите позицията (индекса) на съвпадащите части.
Самият итератор не може да бъде отпечатан с print(), за да се получи съдържанието му. Ако използвате вградената функция next() или командата for, можете да получите съдържанието едно по едно.
s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'
result = re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s)
print(result)
# <callable_iterator object at 0x10b0efa90>
print(type(result))
# <class 'callable_iterator'>
for m in result:
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
# <re.Match object; span=(13, 24), match='bbb@yyy.com'>
# <re.Match object; span=(26, 37), match='ccc@zzz.net'>
Той може да бъде превърнат в списък и с функцията list().
l = list(re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s))
print(l)
# [<re.Match object; span=(0, 11), match='aaa@xxx.com'>, <re.Match object; span=(13, 24), match='bbb@yyy.com'>, <re.Match object; span=(26, 37), match='ccc@zzz.net'>]
print(l[0])
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
print(type(l[0]))
# <class 're.Match'>
print(l[0].span())
# (0, 11)
Ако искате да получите позицията на всички съвпадащи части, записът за разбиране на списък е по-удобен от list().
print([m.span() for m in re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s)])
# [(0, 11), (13, 24), (26, 37)]
Итераторът изважда елементите в определен ред. Имайте предвид, че ако се опитате да извлечете още елементи след достигане на края, ще останете без нищо.
result = re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s)
for m in result:
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
# <re.Match object; span=(13, 24), match='bbb@yyy.com'>
# <re.Match object; span=(26, 37), match='ccc@zzz.net'>
print(list(result))
# []
Заменете съответстващите части: sub(), subn()
С помощта на функцията sub() можете да замените съответстващата част с друг низ. Заместеният низ ще бъде върнат.
s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'
result = re.sub(r'[a-z]+@[a-z]+\.com', 'new-address', s)
print(result)
# new-address, new-address, ccc@zzz.net
print(type(result))
# <class 'str'>
При групиране със скоби() съвпадналият низ може да се използва в заменения низ.
По подразбиране се поддържа следното: Имайте предвид, че за нормални низове, които не са необработени низове, трябва да се изпише обратна наклонена черта преди обратната наклонена черта, за да се избегне обратната наклонена черта.
\1 | Първата скоба |
\2 | Втората скоба |
\3 | Третата скоба |
result = re.sub(r'([a-z]+)@([a-z]+)\.com', r'\1@\2.net', s)
print(result)
# aaa@xxx.net, bbb@yyy.net, ccc@zzz.net
?P<xxx>
Ако дадете име на групата, като го напишете в началото на скобите на шаблона за регулярни изрази, можете да го зададете, като използвате името вместо числото, както е показано по-долу.\g<xxx>
result = re.sub(r'(?P<local>[a-z]+)@(?P<SLD>[a-z]+)\.com', r'\g<local>@\g<SLD>.net', s)
print(result)
# aaa@xxx.net, bbb@yyy.net, ccc@zzz.net
Аргументът count задава максималния брой замени. Ще бъде заменен само броят от лявата страна.
result = re.sub(r'[a-z]+@[a-z]+\.com', 'new-address', s, count=1)
print(result)
# new-address, bbb@yyy.com, ccc@zzz.net
subn() връща кортеж от заместения низ (същия като върнатата стойност на sub()) и броя на заместените части (броя на частите, които отговарят на шаблона).
result = re.subn(r'[a-z]+@[a-z]+\.com', 'new-address', s)
print(result)
# ('new-address, new-address, ccc@zzz.net', 2)
Методът за задаване на аргументи е същият като при sub(). Можете да използвате частта, групирана в скоби, или да посочите броя на аргументите.
result = re.subn(r'(?P<local>[a-z]+)@(?P<SLD>[a-z]+)\.com', r'\g<local>@\g<SLD>.net', s)
print(result)
# ('aaa@xxx.net, bbb@yyy.net, ccc@zzz.net', 2)
result = re.subn(r'[a-z]+@[a-z]+\.com', 'new-address', s, count=1)
print(result)
# ('new-address, bbb@yyy.com, ccc@zzz.net', 1)
Разделяне на низове с шаблони на регулярни изрази: split()
Сплит() разделя низа в частта, която отговаря на шаблона, и го връща като списък.
Обърнете внимание, че първото и последното съвпадение ще съдържат празни низове в началото и в края на получения списък.
s = '111aaa222bbb333'
result = re.split('[a-z]+', s)
print(result)
# ['111', '222', '333']
result = re.split('[0-9]+', s)
print(result)
# ['', 'aaa', 'bbb', '']
Аргументът maxsplit задава максималния брой разделяния (части). Само броят от лявата страна ще бъде разделен.
result = re.split('[a-z]+', s, 1)
print(result)
# ['111', '222bbb333']
Метасимволи на регулярни изрази, специални последователности и предупреждения в Python
Основните метасимволи на регулярните изрази (специални символи) и специални последователности, които могат да се използват в модула Python 3 re, са следните
метасимвол | съдържание |
---|---|
. | Всеки единичен символ, различен от нов ред (включително нов ред с флаг DOTALL) |
^ | Началото на низа (съвпада и с началото на всеки ред с флага MULTILINE) |
$ | Краят на низа (съвпада и с края на всеки ред с флага MULTILINE) |
* | Повтаряне на предишния модел повече от 0 пъти |
+ | Повторете предишния модел поне веднъж. |
? | Повтаряне на предишния модел 0 или 1 път |
{m} | Повторете предишния модел m пъти |
{m, n} | Последният модел.m ~n повторете |
[] | Набор от символи[] Съвпада с някой от тези знаци |
| | ИЛИA|B Съвпада с модел A или B |
специална последователност | съдържание |
---|---|
\d | Десетични числа в Unicode (ограничени до ASCII числа с флаг ASCII) |
\D | \d Тоест обратното на това. |
\s | Знаци за бели полета в Unicode (ограничени до знаци за бели полета в ASCII чрез флаг ASCII) |
\S | \s Тоест обратното на това. |
\w | Символи за думи Unicode и подчертавания (ограничени до буквено-цифрови символи ASCII и подчертавания с флаг ASCII) |
\W | \w Тоест обратното на това. |
Не всички от тях са изброени в тази таблица. Вижте официалната документация за пълен списък.
Също така имайте предвид, че някои от значенията са различни в Python 2.
Задаване на флага
Както е показано в таблицата по-горе, някои метасимволи и специални последователности променят режима си в зависимост от флага.
Тук са разгледани само основните знамена. За останалите вижте официалната документация.
Ограничено до ASCII символи: re.ASCII
\w
По подразбиране за низове в Python 3 това ще съответства и на двубайтови канджи, буквено-цифрови символи и т.н. Той не е еквивалентен на следния, защото не е стандартен регулярен израз.[a-zA-Z0-9_]
m = re.match(r'\w+', '漢字ABC123')
print(m)
# <re.Match object; span=(0, 11), match='漢字ABC123'>
m = re.match('[a-zA-Z0-9_]+', '漢字ABC123')
print(m)
# None
Ако зададете re.ASCII за флаговете на аргумента във всяка функция или добавите следния флаг за вграждане в началото на низ от шаблони на регулярни изрази, той ще съответства само на ASCII символи (няма да съответства на двубайтови японски символи, буквено-цифрови символи и т.н.).(?a)
В този случай следните две са еквивалентни.\w
=[a-zA-Z0-9_]
m = re.match(r'\w+', '漢字ABC123', flags=re.ASCII)
print(m)
# None
m = re.match(r'(?a)\w+', '漢字ABC123')
print(m)
# None
Същото важи и при компилиране с re.compile(). Използвайте аргумента flags или inline flags.
p = re.compile(r'\w+', flags=re.ASCII)
print(p)
# re.compile('\\w+', re.ASCII)
print(p.match('漢字ABC123'))
# None
p = re.compile(r'(?a)\w+')
print(p)
# re.compile('(?a)\\w+', re.ASCII)
print(p.match('漢字ABC123'))
# None
ASCII се предлага и като кратка форма re. A. Можете да използвате.
print(re.ASCII is re.A)
# True
\W, обратното на \W, също се влияе от re.ASCII и инлайн флаговете.
m = re.match(r'\W+', '漢字ABC123')
print(m)
# None
m = re.match(r'\W+', '漢字ABC123', flags=re.ASCII)
print(m)
# <re.Match object; span=(0, 11), match='漢字ABC123'>
Както и при \w, следните два символа по подразбиране съвпадат както с еднобайтови, така и с двубайтови символи, но се ограничават до еднобайтови символи, ако са зададени re.ASCII или флагове за вграждане.
- Съвпадение на числата
\d
- съвпада с празно място
\s
- Съвпада с числа, които не са числа
\D
- Съответства на всяко място, което не е интервал.
\S
m = re.match(r'\d+', '123')
print(m)
# <re.Match object; span=(0, 3), match='123'>
m = re.match(r'\d+', '123')
print(m)
# <re.Match object; span=(0, 3), match='123'>
m = re.match(r'\d+', '123', flags=re.ASCII)
print(m)
# <re.Match object; span=(0, 3), match='123'>
m = re.match(r'\d+', '123', flags=re.ASCII)
print(m)
# None
m = re.match(r'\s+', ' ') # full-width space
print(m)
# <re.Match object; span=(0, 1), match='\u3000'>
m = re.match(r'\s+', ' ', flags=re.ASCII)
print(m)
# None
Не се различават големи и малки букви:re.IGNORECASE
По подразбиране тя е чувствителна към малки и големи букви. За да съвпаднат и двете, трябва да включите в шаблона както главни, така и малки букви.
re.IGNORECASE
Ако това е посочено, ще се извърши сравнение без значение на регистрите. Еквивалентно на флага i в стандартните регулярни изрази.
m = re.match('[a-zA-Z]+', 'abcABC')
print(m)
# <re.Match object; span=(0, 6), match='abcABC'>
m = re.match('[a-z]+', 'abcABC', flags=re.IGNORECASE)
print(m)
# <re.Match object; span=(0, 6), match='abcABC'>
m = re.match('[A-Z]+', 'abcABC', flags=re.IGNORECASE)
print(m)
# <re.Match object; span=(0, 6), match='abcABC'>
Можете да използвате по-малко от или равно на.
- вграден флаг
(?i)
- съкращение
re.I
Подберете началото и края на всеки ред:re.MULTILINE
^
Метасимволите в този регулярен израз съвпадат с началото на низа.
По подразбиране се подбира само началото на целия низ, но следният начин ще подбере и началото на всеки ред. Еквивалентно на флага m в стандартните регулярни изрази.re.MULTILINE
s = '''aaa-xxx
bbb-yyy
ccc-zzz'''
print(s)
# aaa-xxx
# bbb-yyy
# ccc-zzz
result = re.findall('[a-z]+', s)
print(result)
# ['aaa', 'xxx', 'bbb', 'yyy', 'ccc', 'zzz']
result = re.findall('^[a-z]+', s)
print(result)
# ['aaa']
result = re.findall('^[a-z]+', s, flags=re.MULTILINE)
print(result)
# ['aaa', 'bbb', 'ccc']
$
Съответства на края на низа. По подразбиране се сравнява само краят на целия низ.re.MULTILINE
Ако посочите тази стойност, тя ще съответства и на края на всеки ред.
result = re.findall('[a-z]+$', s)
print(result)
# ['zzz']
result = re.findall('[a-z]+$', s, flags=re.MULTILINE)
print(result)
# ['xxx', 'yyy', 'zzz']
Можете да използвате по-малко от или равно на.
- вграден флаг
(?m)
- съкращение
re.M
Задаване на няколко флага
|
Ако искате да активирате няколко флага едновременно, използвайте това. В случай на вградени флагове всеки символ трябва да бъде последван от буква, както е показано по-долу.(?am)
s = '''aaa-xxx
漢漢漢-字字字
bbb-zzz'''
print(s)
# aaa-xxx
# 漢漢漢-字字字
# bbb-zzz
result = re.findall(r'^\w+', s, flags=re.M)
print(result)
# ['aaa', '漢漢漢', 'bbb']
result = re.findall(r'^\w+', s, flags=re.M | re.A)
print(result)
# ['aaa', 'bbb']
result = re.findall(r'(?am)^\w+', s)
print(result)
# ['aaa', 'bbb']
Жадни и нежадни съвпадения
Това е общ проблем с регулярните изрази, а не само проблем на Python, но ще пиша за него, защото имам склонност да си навличам неприятности.
По подразбиране следното е алчно съвпадение, което съвпада с най-дългия възможен низ.
*
+
?
s = 'aaa@xxx.com, bbb@yyy.com'
m = re.match(r'.+com', s)
print(m)
# <re.Match object; span=(0, 24), match='aaa@xxx.com, bbb@yyy.com'>
print(m.group())
# aaa@xxx.com, bbb@yyy.com
Символът ? след него ще доведе до минимално съвпадение, което не е жадно, а съответства на най-краткия възможен низ.
*?
+?
??
m = re.match(r'.+?com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
print(m.group())
# aaa@xxx.com
Обърнете внимание, че алчното съвпадение по подразбиране може да съвпадне с неочаквани низове.