Como usar o módulo de expresión regular de Python re (coincidir, buscar, sub, etc.)

Negocios

Para realizar o procesamento de expresións regulares en Python, usamos o módulo re da biblioteca estándar. Permítelle extraer, substituír e dividir cadeas utilizando patróns de expresión regular.

Nesta sección, primeiro explicaremos as funcións e os métodos do módulo re.

  • Compilación de patróns de expresión regular:compile()
  • obxecto coincidente
  • Comproba se o inicio da cadea coincide, extrae:match()
  • Comprobe as coincidencias non limitadas ao principio:search()
  • Comproba se a cadea enteira coincide:fullmatch()
  • Obtén unha lista de todas as pezas coincidentes:findall()
  • Obtén todas as partes coincidentes como iterador:finditer()
  • Substitúe a peza correspondente:sub(),subn()
  • Dividir cadeas con patróns de expresión regular:split()

Despois diso, explicarei os metacaracteres (caracteres especiais) e as secuencias especiais de expresións regulares que se poden usar no módulo re. Basicamente, é a sintaxe estándar de expresións regulares, pero teña coidado coa configuración de bandeiras (especialmente re.ASCII).

  • Metacaracteres de expresións regulares, secuencias especiais e advertencias en Python
  • Colocando a bandeira
    • Limitado a caracteres ASCII:re.ASCII
    • Non distingue entre maiúsculas e minúsculas:re.IGNORECASE
    • Relaciona o comezo e o final de cada liña:re.MULTILINE
    • Especifique varias bandeiras
  • Partidos golosos e non golosos

Compile o patrón de expresión regular: compile()

Existen dúas formas de realizar o procesamento de expresións regulares no módulo re.

Executar con función

O primeiro é unha función.re.match(),re.sub()Funcións como estas están dispoñibles para realizar extracción, substitución e outros procesos mediante patróns de expresión regular.

Os detalles das funcións describiranse máis adiante, pero en todas elas, o primeiro argumento é a cadea do patrón de expresión regular, seguida da cadea que se vai procesar, etc. Por exemplo, en re.sub(), que realiza a substitución, o segundo argumento é a cadea de substitución e o terceiro argumento é a cadea que se vai procesar.

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

Teña en conta que [a-z] no patrón de expresións regulares deste exemplo significa calquera carácter de a a z (é dicir, alfabeto en minúscula) e + significa repetir o patrón anterior (neste caso [a-z]) unha ou máis veces. [a-z]+ coincide con calquera cadea que repita un ou máis caracteres alfabéticos en minúsculas.

. é un meta carácter (un carácter con significado especial) e debe escaparse cunha barra invertida.

Dado que as cadeas de patróns de expresións regulares adoitan usar moitas barras inclinadas invertidas, é conveniente usar cadeas en bruto como no exemplo.

Execútase nun método dun obxecto de patrón de expresión regular

A segunda forma de procesar expresións regulares no módulo re é o método do obxecto patrón de expresións regulares.

Usando re.compile(), pode compilar unha cadea de patrón de expresión regular para crear un obxecto de patrón de expresión regular.

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()Por exemplo, o mesmo proceso que estas funcións pódese executar como métodos match(),sub() de obxectos de expresión regular.

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

Todas as funcións re.xxx() descritas a continuación tamén se proporcionan como métodos do obxecto de expresión regular.

Se estás a repetir un proceso que usa o mesmo patrón, é máis eficiente xerar un obxecto de expresión regular con re.compile() e usalo.

No seguinte código de exemplo, a función úsase sen compilar por comodidade, pero se quere utilizar o mesmo patrón repetidamente, recoméndase compilalo previamente e executalo como método dun obxecto de expresión regular.

obxecto coincidente

match(), search(), etc. devolven un obxecto coincidente.

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'>

A cadea e a posición coincidentes obtéñense mediante os seguintes métodos do obxecto de coincidencia.

  • Obtén a localización do partido:start(),end(),span()
  • Obter a cadea coincidente:group()
  • Obtén a cadea para cada grupo:groups()
print(m.start())
# 0

print(m.end())
# 11

print(m.span())
# (0, 11)

print(m.group())
# aaa@xxx.com

Se encerra unha parte dun patrón de expresión regular nunha cadea con parénteses(), a parte procesarase como un grupo. Neste caso, a cadea da parte que coincide con cada grupo en groups() pódese obter como unha tupla.

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')

Comproba se o inicio dunha cadea coincide, extrae: match()

match() devolve un obxecto de coincidencia se o inicio da cadea coincide co patrón.

Como se mencionou anteriormente, o obxecto de coincidencia pódese usar para extraer a subcadea coincidente ou simplemente para comprobar se se fixo unha coincidencia.

match() só comprobará o principio. Se non hai ningunha cadea coincidente ao principio, devolve Ningún.

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

Buscar coincidencias non limitadas ao principio, extraer: buscar()

Do mesmo xeito que match(), devolve un obxecto de coincidencia se coincide.

Se hai varias partes coincidentes, só se devolverá a primeira parte coincidente.

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'>

Se queres obter todas as partes coincidentes, usa findall() ou finditer() como se describe a continuación.

Comproba se toda a cadea coincide: fullmatch()

Para comprobar se toda a cadea coincide co patrón de expresión regular, use fullmatch(). Isto é útil, por exemplo, para comprobar se unha cadea é válida como enderezo de correo electrónico ou non.

Se a cadea enteira coincide, devólvese un obxecto de coincidencia.

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'>

Se hai partes non coincidentes (só coincidencias parciais ou ningunha coincidencia), non se devolve ningunha.

s = '!!!aaa@xxx.com!!!'

m = re.fullmatch(r'[a-z]+@[a-z]+\.com', s)
print(m)
# None

O fullmatch() engadiuse en Python 3.4. Se queres facer o mesmo en versións anteriores, utiliza match() e un metacarácter $ coincidente ao final. Se a cadea enteira de principio a fin non coincide, devolve Ningún.

s = '!!!aaa@xxx.com!!!'

m = re.match(r'[a-z]+@[a-z]+\.com$', s)
print(m)
# None

Obter unha lista de todas as partes coincidentes: findall()

findall() devolve unha lista de todas as subcadeas coincidentes. Teña en conta que os elementos da lista non son obxectos coincidentes senón cadeas.

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']

O número de pezas coincidentes pódese comprobar mediante a función integrada len(), que devolve o número de elementos da lista.

print(len(result))
# 3

A agrupación con parénteses() nun patrón de expresión regular devolve unha lista de tuplas cuxos elementos son as cadeas de cada grupo. Isto é equivalente a groups() no obxecto de coincidencia.

result = re.findall(r'([a-z]+)@([a-z]+)\.([a-z]+)', s)
print(result)
# [('aaa', 'xxx', 'com'), ('bbb', 'yyy', 'com'), ('ccc', 'zzz', 'net')]

Os parénteses do grupo () pódense aniñar, polo que se queres obter tamén a coincidencia completa, só tes que incluír a coincidencia completa entre parénteses ().

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')]

Se non se atopa ningunha coincidencia, devólvese unha tupla baleira.

result = re.findall('[0-9]+', s)
print(result)
# []

Obter todas as partes coincidentes como un iterador: finditer()

finditer() devolve todas as partes coincidentes como un iterador. Os elementos non son cadeas como findall(), senón que coinciden con obxectos, polo que pode obter a posición (índice) das partes coincidentes.

O propio iterador non se pode imprimir con print() para obter o seu contido. Se usa a función incorporada next() ou a instrución for, pode obter o contido un por un.

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'>

Tamén se pode converter nunha lista con 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)

Se queres obter a posición de todas as partes coincidentes, a notación de comprensión da lista é máis conveniente que list().

print([m.span() for m in re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s)])
# [(0, 11), (13, 24), (26, 37)]

O iterador saca os elementos en orde. Teña en conta que se tentas extraer máis elementos despois de chegar ao final, quedarás sen nada.

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))
# []

Substitúe as partes coincidentes: sub(), subn()

Usando sub(), pode substituír a parte coincidente por outra cadea. Devolverase a cadea substituída.

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'>

Cando se agrupa con parénteses(), a cadea coincidente pódese usar na cadea substituída.

De xeito predeterminado, admítese o seguinte: Teña en conta que para as cadeas normais que non son cadeas sen formato, debe aparecer unha barra invertida antes da barra invertida para escapar da barra invertida.

\1O primeiro paréntese
\2O segundo paréntese
\3O terceiro paréntese
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>
Se nomeas o grupo escribindo isto ao principio dos parénteses do patrón de expresión regular, podes especificalo usando o nome en lugar do número, como se mostra a continuación.
\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

O reconto de argumentos especifica o número máximo de substitucións. Só se substituirá o reconto do lado esquerdo.

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() devolve unha tupla da cadea substituída (o mesmo que o valor de retorno de sub()) e o número de partes substituídas (o número que coincide co patrón).

result = re.subn(r'[a-z]+@[a-z]+\.com', 'new-address', s)
print(result)
# ('new-address, new-address, ccc@zzz.net', 2)

O método para especificar argumentos é o mesmo que sub(). Podes usar a parte agrupada por parénteses ou especificar o reconto de argumentos.

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)

Dividir cadeas con patróns de expresión regular: split()

split() divide a cadea na parte que coincide co patrón e devólvaa como unha lista.

Teña en conta que a primeira e a última coincidencia conterán cadeas baleiras ao principio e ao final da lista resultante.

s = '111aaa222bbb333'

result = re.split('[a-z]+', s)
print(result)
# ['111', '222', '333']

result = re.split('[0-9]+', s)
print(result)
# ['', 'aaa', 'bbb', '']

O argumento maxsplit especifica o número máximo de divisións (pezas). Só se dividirá o reconto do lado esquerdo.

result = re.split('[a-z]+', s, 1)
print(result)
# ['111', '222bbb333']

Metacaracteres de expresións regulares, secuencias especiais e advertencias en Python

Os principais metacaracteres de expresión regular (caracteres especiais) e as secuencias especiais que se poden usar no módulo Python 3 re son os seguintes

metacaráctercontidos
.Calquera carácter que non sexa unha nova liña (incluída unha nova liña coa marca DOTALL)
^O inicio da cadea (tamén fai coincidir o comezo de cada liña coa bandeira MULTILINE)
$O final da cadea (tamén fai coincidir o final de cada liña coa bandeira MULTILINE)
*Repita o patrón anterior máis de 0 veces
+Repita o patrón anterior polo menos unha vez.
?Repita o patrón anterior 0 ou 1 veces
{m}Repita o patrón anterior m veces
{m, n}O último patrón.m~nrepetir
[]Un conxunto de personaxes[]Coincide con calquera destes personaxes
|OUA|BCoincide con patróns A ou B
secuencia especialcontidos
\dNúmeros decimais Unicode (limitados a números ASCII pola bandeira ASCII)
\D\dSignificando o contrario disto.
\sCaracteres de espazo en branco Unicode (limitados a caracteres de espazo en branco ASCII pola marca ASCII)
\S\sSignificando o contrario disto.
\wCaracteres de palabras Unicode e guións baixos (limitados a caracteres alfanuméricos ASCII e guións baixos pola bandeira ASCII)
\W\wSignificando o contrario disto.

Non todos están enumerados nesta táboa. Consulte a documentación oficial para obter unha lista completa.

Teña en conta tamén que algúns dos significados son diferentes en Python 2.

Colocando a bandeira

Como se mostra na táboa anterior, algúns metacaracteres e secuencias especiais cambian o seu modo dependendo da bandeira.

Aquí só se tratan as bandeiras principais. Consulta a documentación oficial para o resto.

Limitado a caracteres ASCII: re.ASCII

\wIsto tamén coincidirá con kanji de dobre byte, caracteres alfanuméricos, etc. por defecto para as cadeas de Python 3. Non é equivalente ao seguinte porque non é unha expresión regular estándar.[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

Se especifica re.ASCII para os indicadores de argumento en cada función, ou engade a seguinte marca en liña ao comezo da cadea do patrón de expresións regulares, só coincidirá con caracteres ASCII (non coincidirá con xaponés de dobre byte, caracteres alfanuméricos, etc. .).
(?a)
Neste caso, os dous seguintes son equivalentes.
\w#ERROR![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

O mesmo aplícase cando se compila con re.compile(). Use o argumento bandeiras ou bandeiras en liña.

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 tamén está dispoñible como a forma curta re. R. Podes usar calquera dos dous.

print(re.ASCII is re.A)
# True

\W, o contrario de \W, tamén se ve afectado por marcas re.ASCII e inline.

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'>

Do mesmo xeito que con \w, os dous seguintes coinciden con caracteres de byte único e de dobre byte por defecto, pero están limitados a caracteres dun só byte se se especifican marcas re.ASCII ou en liña.

  • Relaciona os números\d
  • Coincide cun espazo en branco\s
  • Coincide con non números\D
  • Coincide con calquera que non sexa espazo.\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

Non distingue entre maiúsculas e minúsculas:re.IGNORECASE

Por defecto, distingue entre maiúsculas e minúsculas. Para facer coincidir ambos, cómpre incluír letras maiúsculas e minúsculas no patrón.

re.IGNORECASESe se especifica isto, coincidirá sen distinción entre maiúsculas e minúsculas. Equivale á bandeira i nas expresións regulares estándar.

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'>

Podes usar menos ou igual a.

  • bandeira en liña(?i)
  • abreviaturare.I

Relaciona o comezo e o final de cada liña:re.MULTILINE

^Os metacaracteres desta expresión regular coinciden co inicio da cadea.

De xeito predeterminado, só coincide o comezo de toda a cadea, pero o seguinte coincidirá tamén co comezo de cada liña. Equivale á bandeira m nas expresións regulares estándar.
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']

$Coincide co final da corda. De xeito predeterminado, só coincide o final de toda a cadea.
re.MULTILINESe o especificas, tamén coincidirá co final de cada liña.

result = re.findall('[a-z]+$', s)
print(result)
# ['zzz']

result = re.findall('[a-z]+$', s, flags=re.MULTILINE)
print(result)
# ['xxx', 'yyy', 'zzz']

Podes usar menos ou igual a.

  • bandeira en liña(?m)
  • abreviaturare.M

Especifique varias bandeiras

|Se queres activar varias bandeiras ao mesmo tempo, úsao. No caso das bandeiras en liña, cada carácter debe ir seguido dunha letra como se mostra a continuación.
(?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']

Partidos golosos e non golosos

Este é un problema xeral coas expresións regulares, non só un problema con Python, pero vou escribir sobre el porque adoita meterme en problemas.

Por defecto, a seguinte é unha coincidencia codiciosa, que coincide coa cadea máis longa posible.

  • *
  • +
  • ?
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

O ? despois dará lugar a unha coincidencia mínima e non codiciosa, que coincida coa cadea máis curta posible.

  • *?
  • +?
  • ??
m = re.match(r'.+?com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

print(m.group())
# aaa@xxx.com

Teña en conta que a coincidencia codiciosa predeterminada pode coincidir con cadeas inesperadas.