Expressões regulares
import re
Este caderno foi traduzido automaticamente para torná-lo acessível a mais pessoas, por favor me avise se você vir algum erro de digitação..
Métodos
Localizar
Com o método findall()
, podemos encontrar todas as correspondências de um padrão em uma string.
import restring = "Hola, soy un string"print(re.findall("Hola, soy", string))
['Hola, soy']
Pesquisa
Mas se quisermos encontrar a posição em que um padrão está localizado, podemos usar o método search()
para procurar um padrão em uma string. Esse método retorna um objeto Match se encontrar uma correspondência, caso contrário, retorna None.
print(re.search("soy", string))
<re.Match object; span=(6, 9), match='soy'>
Correspondência
Também podemos usar o método match()
que procura o padrão no início da string.
print(re.match("Hola", string))print(re.match("soy", string))
<re.Match object; span=(0, 4), match='Hola'>None
Span
Se quisermos obter a posição da correspondência, podemos usar o método span()
que retorna uma tupla com a posição inicial e final da correspondência.
print(re.match("Hola", string).span())
(0, 4)
Grupo
Sabendo a posição da correspondência, podemos usar o método group()
para obter a substring que corresponde ao padrão.
print(re.match("Hola", string).group())
Hola
Também poderíamos usar o início e o fim da correspondência para criar uma fatia da cadeia de caracteres.
start, end = re.match("Hola", string).span()print(string[start:end])
Hola
Separação
Com o método split()
, podemos dividir uma string em uma lista de substrings usando um padrão como separador.
split = re.split("soy", string)print(split)
['Hola, ', ' un string']
A frase foi dividida em duas cadeias de caracteres usando "I am" como separador.
Sub
Com o método sub()
, podemos substituir todas as correspondências de um padrão por outra substring.
sub = re.sub("soy", "eres", string)print(sub)
Hola, eres un string
Ele substituiu todas as correspondências de "I am" por "you are".
Padrões
O caractere .
Com o caractere .
, podemos pesquisar qualquer caractere; qualquer caractere em nossa string será encontrado.
string = "Hola, soy un string"print(re.findall(".", string))
['H', 'o', 'l', 'a', ',', ' ', 's', 'o', 'y', ' ', 'u', 'n', ' ', 's', 't', 'r', 'i', 'n', 'g']
Se, por exemplo, quisermos sequências de dois caracteres, faremos a busca com dois .
s em uma linha.
string1 = "Hola, soy un string"string2 = "Hola, soy un string2"print(re.findall("..", string1))print(re.findall("..", string2))
['Ho', 'la', ', ', 'so', 'y ', 'un', ' s', 'tr', 'in']['Ho', 'la', ', ', 'so', 'y ', 'un', ' s', 'tr', 'in', 'g2']
Como podemos ver, string1
tem um número ímpar de caracteres, de modo que o último g
não é usado, mas string2
tem um número par de caracteres, de modo que todos os caracteres são usados.
Vamos analisar isso de outra forma: vamos alterar cada sequência de três caracteres para um símbolo $
.
print(string1)print(re.sub("...", "$ ", string1))
Hola, soy un string$ $ $ $ $ $ g
Imprimi dois espaços após cada $
para que você possa ver a alteração, e você pode ver como o último caractere não o converte.
As classes predefinidas e construídas
Dígito
Se quisermos encontrar os dígitos, precisaremos usar d
.
string = "Hola, soy un string con 123 digitos"print(re.findall("d", string))
['1', '2', '3']
Como antes, se, por exemplo, quisermos dois dígitos, colocamos d
duas vezes.
print(re.findall("dd", string))
['12']
Carta
Se quisermos encontrar letras, precisamos usar w
. Palavra significa todas as letras de a
a z
, de A
a Z
, números de 0
a 9
e o _
.
string = "Hola, soy un_string con, 123 digitos"print(re.findall("w", string))
['H', 'o', 'l', 'a', 's', 'o', 'y', 'u', 'n', '_', 's', 't', 'r', 'i', 'n', 'g', 'c', 'o', 'n', '1', '2', '3', 'd', 'i', 'g', 'i', 't', 'o', 's']
Como podemos ver, ele usa tudo, exceto os espaços e a vírgula.
Espaços
Se quisermos encontrar espaços, precisamos de "espaços".
string = "Hola, soy un_string con, 123 digitos"print(re.sub("s", "*", string))
Hola,*soy*un_string*con,*123*digitos
As expressões regulares tratam as quebras de linha como espaços.
string = """Hola, soy un stringcon un salto de línea"""print(re.sub("s", "*", string))
Hola,*soy*un*string**con*un*salto*de*línea
Ranks
Se quisermos pesquisar um intervalo, usaremos []
, por exemplo, se quisermos os números de 4 a 8, usaremos
string = "1234567890"print(re.findall("[4-8]", string))
['4', '5', '6', '7', '8']
Podemos ampliar o intervalo de pesquisa
string = "1234567890"print(re.findall("[2-57-9]", string))
['2', '3', '4', '5', '7', '8', '9']
Se também quisermos encontrar um caractere específico, colocaremos o caractere seguido de ``.
string = "1234567890."print(re.findall("[2-57-9.]", string))
['2', '3', '4', '5', '7', '8', '9', '.']
Colchete [
e colchete ]
Como vimos, se quisermos encontrar intervalos, usamos []
, mas e se quisermos encontrar apenas o [
ou o ]
? Para isso, temos que usar []
e [
]`.
string = "[1234567890]"print(re.findall("[", string))print(re.findall("]", string))
['['][']']
Os delimitadores +
, *
, ?
, `?
Star *
(nenhum ou todos)
O delimitador *
indica que você deseja pesquisar nenhum ou todos eles, e não um a um como antes.
string = "Hola, soy un string con 12 123 digitos"print(re.findall("d", string))print(re.findall("d*", string))
['1', '2', '1', '2', '3']['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '12', '', '123', '', '', '', '', '', '', '', '', '']
Como você pode ver, colocar o *
encontrou todas as posições em que há zero caracteres ou todos os caracteres.
Plus +
(um ou mais)
Com o delimitador +
, você indica que deseja pesquisar um ou mais
string = "Hola, soy un string con 1 12 123 digitos"print(re.findall("d+", string))
['1', '12', '123']
Opcional ?
(zero ou um)
O delimitador ?
indica que você deseja pesquisar zero ou um.
string = "Hola, soy un string con 1 12 123 digitos"print(re.sub("d?", "-", string))
-H-o-l-a-,- -s-o-y- -u-n- -s-t-r-i-n-g- -c-o-n- -- --- ---- -d-i-g-i-t-o-s-
Contadores
Quando quisermos encontrar algo que apareça x vezes, usaremos os contadores entre chaves {}
. Por exemplo, se quisermos encontrar uma sequência na qual haja pelo menos dois dígitos
string = "Hola, soy un string con 1 12 123 1234 1234digitos"print(re.findall("d{2}", string))
['12', '12', '12', '34', '12', '34']
Como você pode ver, você encontrou as sequências 12
e 34
.
Os contadores aceitam uma dimensão superior e inferior {{inf, sup}
.
string = "Hola, soy un string con 1 12 123 1234 1234digitos"print(re.findall("d{2,5}", string))
['12', '123', '1234', '1234']
Se nenhuma dimensão superior for definida, isso significa que você deseja pelo menos o número de elementos indicado, mas nenhum limite superior.
string = "Hola, soy un string con 1 12 123 1234 12345464168415641646451563416 digitos"print(re.findall("d{2,}", string))
['12', '123', '1234', '12345464168415641646451563416']
Se quisermos usar a notação de dimensão superior e inferior, mas quisermos um número fixo, teremos que colocar esse número em ambas as dimensões.
string = "Hola, soy un string con 1 12 123 1234 12345464168415641646451563416 digitos"print(re.findall("d{2,3}", string))
['12', '123', '123', '123', '454', '641', '684', '156', '416', '464', '515', '634', '16']
Classes
Você pode criar classes usando colchetes []
. Na verdade, vimos que isso era para intervalos, mas, depois de definir o que deseja dentro deles, você pode considerá-lo como uma classe e operar com o []
.
Por exemplo, suponha que tenhamos um número de telefone, que pode ser fornecido de uma das seguintes maneiras
- 666-66-66-66
- 666-666-666
- 666 666 666
- 666 66 66 66
- 666666666
Há muitas maneiras de fornecer um número, portanto, vamos ver como criar uma classe para definir o delimitador.
Primeiro, pediremos a ele que encontre todas as sequências de números em que haja pelo menos dois números.
string1 = "666-66-66-66"string2 = "666-666-666"string3 = "666 66 66 66"string4 = "666 666 666"string5 = "666666666"print(f"string1: {string1} -->", re.findall("d{2,}", string1))print(f"string2: {string2} -->", re.findall("d{2,}", string2))print(f"string3: {string3} -->", re.findall("d{2,}", string3))print(f"string4: {string4} -->", re.findall("d{2,}", string4))print(f"string5: {string5} -->", re.findall("d{2,}", string5))
string1: 666-66-66-66 --> ['666', '66', '66', '66']string2: 666-666-666 --> ['666', '666', '666']string3: 666 66 66 66 --> ['666', '66', '66', '66']string4: 666 666 666 --> ['666', '666', '666']string5: 666666666 --> ['666666666']
Agora, definimos o separador a ser encontrado como um -
ou um espaço.
string1 = "666-66-66-66"string2 = "666-666-666"string3 = "666 66 66 66"string4 = "666 666 666"string5 = "666666666"print(f"string1: {string1} -->", re.findall("[-s]", string1))print(f"string2: {string2} -->", re.findall("[-s]", string2))print(f"string3: {string3} -->", re.findall("[-s]", string3))print(f"string4: {string4} -->", re.findall("[-s]", string4))print(f"string5: {string5} -->", re.findall("[-s]", string5))
string1: 666-66-66-66 --> ['-', '-', '-']string2: 666-666-666 --> ['-', '-']string3: 666 66 66 66 --> [' ', ' ', ' ']string4: 666 666 666 --> [' ', ' ']string5: 666666666 --> []
Como você pode ver na última string, ela não foi encontrada, então adicionamos um ?
para encontrar quando há zero ou um.
string1 = "666-66-66-66"string2 = "666-666-666"string3 = "666 66 66 66"string4 = "666 666 666"string5 = "666666666"print(f"string1: {string1} -->", re.findall("[-s]?", string1))print(f"string2: {string2} -->", re.findall("[-s]?", string2))print(f"string3: {string3} -->", re.findall("[-s]?", string3))print(f"string4: {string4} -->", re.findall("[-s]?", string4))print(f"string5: {string5} -->", re.findall("[-s]?", string5))
string1: 666-66-66-66 --> ['', '', '', '-', '', '', '-', '', '', '-', '', '', '']string2: 666-666-666 --> ['', '', '', '-', '', '', '', '-', '', '', '', '']string3: 666 66 66 66 --> ['', '', '', ' ', '', '', ' ', '', '', ' ', '', '', '']string4: 666 666 666 --> ['', '', '', ' ', '', '', '', ' ', '', '', '', '']string5: 666666666 --> ['', '', '', '', '', '', '', '', '', '']
Agora estamos procurando que tudo esteja junto
string1 = "666-66-66-66"string2 = "666-666-666"string3 = "666 66 66 66"string4 = "666 666 666"string5 = "666666666"print(f"string1: {string1} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?", string1))print(f"string2: {string2} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?", string2))print(f"string3: {string3} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?", string3))print(f"string4: {string4} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?", string4))print(f"string5: {string5} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?", string5))
string1: 666-66-66-66 --> ['666-66-66-66']string2: 666-666-666 --> []string3: 666 66 66 66 --> ['666 66 66 66']string4: 666 666 666 --> []string5: 666666666 --> ['666666666']
Como podemos ver em string2
e string4
, ele não encontra nada. Definimos o filtro [\d{2,}[\s]?
4 vezes, ou seja, queremos uma sequência de pelo menos dois números, seguida por zero ou um hífen ou separador de espaço que se repita 4 vezes. Mas na última sequência não há necessidade do [\d{2,}[\s]?
, pois ele nunca terminará um número com um espaço ou um hífen.
string1 = "666-66-66-66"string2 = "666-666-666"string3 = "666 66 66 66"string4 = "666 666 666"string5 = "666666666"print(f"string1: {string1} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d{2,}", string1))print(f"string2: {string2} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d{2,}", string2))print(f"string3: {string3} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d{2,}", string3))print(f"string4: {string4} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d{2,}", string4))print(f"string5: {string5} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d{2,}", string5))
string1: 666-66-66-66 --> ['666-66-66-66']string2: 666-666-666 --> []string3: 666 66 66 66 --> ['666 66 66 66']string4: 666 666 666 --> []string5: 666666666 --> ['666666666']
Ainda não foi encontrado para string2
e string4
. Isso ocorre porque a última coisa no filtro é um d{2,}
, ou seja, após o terceiro separador, esperamos pelo menos dois números, mas isso não acontece em string2
e string4
, então colocamos o seguinte
string1 = "666-66-66-66"string2 = "666-666-666"string3 = "666 66 66 66"string4 = "666 666 666"string5 = "666666666"print(f"string1: {string1} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d*", string1))print(f"string2: {string2} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d*", string2))print(f"string3: {string3} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d*", string3))print(f"string4: {string4} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d*", string4))print(f"string5: {string5} -->", re.findall("d{2,}[-s]?d{2,}[-s]?d{2,}[-s]?d*", string5))
string1: 666-66-66-66 --> ['666-66-66-66']string2: 666-666-666 --> ['666-666-666']string3: 666 66 66 66 --> ['666 66 66 66']string4: 666 666 666 --> ['666 666 666']string5: 666666666 --> ['666666666']
O delimitador ?
como um delimitador rápido
O exemplo acima pode ser filtrado usando d+?[- ]
.
string1 = "666-66-66-66"string2 = "666-666-666"string3 = "666 66 66 66"string4 = "666 666 666"string5 = "666666666"print(f"string1: {string1} -->", re.findall("d+?[- ]", string1))print(f"string2: {string2} -->", re.findall("d+?[- ]", string2))print(f"string3: {string3} -->", re.findall("d+?[- ]", string3))print(f"string4: {string4} -->", re.findall("d+?[- ]", string4))print(f"string5: {string5} -->", re.findall("d+?[- ]", string5))
string1: 666-66-66-66 --> ['666-', '66-', '66-']string2: 666-666-666 --> ['666-', '666-']string3: 666 66 66 66 --> ['666 ', '66 ', '66 ']string4: 666 666 666 --> ['666 ', '666 ']string5: 666666666 --> []
Sem o delimitador ?
, teríamos \d+[- ]
, o que significa uma sequência de um ou mais números seguidos por um espaço ou hífen. Mas o que o delimitador ?
faz é tornar essa busca mais rápida
O negador
Anteriormente, vimos que com d
encontramos dígitos, portanto, com D
encontramos tudo o que não é dígito.
string1 = "E3s4t6e e1s2t3r5i6n7g8 t9i0e4n2e1 d4i5g7i9t0o5s2"print(re.findall("D", string1))
['E', 's', 't', 'e', ' ', 'e', 's', 't', 'r', 'i', 'n', 'g', ' ', 't', 'i', 'e', 'n', 'e', ' ', 'd', 'i', 'g', 'i', 't', 'o', 's']
O mesmo vale para as letras: se você digitar W
, ele encontrará tudo o que não for uma letra.
string1 = "Letras ab27_ no letras ,.:;´ç"print(re.findall("W", string1))
[' ', ' ', ' ', ' ', ',', '.', ':', ';', '´']
Se colocarmos S
, encontraremos tudo o que não for espaço
print(re.findall("S", string1))
['L', 'e', 't', 'r', 'a', 's', 'a', 'b', '2', '7', '_', 'n', 'o', 'l', 'e', 't', 'r', 'a', 's', ',', '.', ':', ';', '´', 'ç']
Mas, caso tenhamos uma classe ou outra coisa, podemos negá-la com ^
.
string1 = "1234567890"print(re.findall("[^5-9]", string1))
['1', '2', '3', '4', '0']
Voltando ao exemplo anterior dos números de telefone, podemos filtrá-los da seguinte forma
string1 = "666-66-66-66"string2 = "666-666-666"string3 = "666 66 66 66"string4 = "666 666 666"string5 = "666666666"print(f"string1: {string1} -->", re.findall("d{2,}D?d{2,}D?d{2,}D?d*", string1))print(f"string2: {string2} -->", re.findall("d{2,}D?d{2,}D?d{2,}D?d*", string2))print(f"string3: {string3} -->", re.findall("d{2,}D?d{2,}D?d{2,}D?d*", string3))print(f"string4: {string4} -->", re.findall("d{2,}D?d{2,}D?d{2,}D?d*", string4))print(f"string5: {string5} -->", re.findall("d{2,}D?d{2,}D?d{2,}D?d*", string5))string5 = "666 666 666"
string1: 666-66-66-66 --> ['666-66-66-66']string2: 666-666-666 --> ['666-666-666']string3: 666 66 66 66 --> ['666 66 66 66']string4: 666 666 666 --> ['666 666 666']string5: 666666666 --> ['666666666']
O que estamos fazendo é solicitar sequências de pelo menos dois dígitos seguidos de um ou nenhum não-dígito.
O início ^
e o final da linha $
.
Com ^
, podemos pesquisar o início de uma linha, por exemplo, se quisermos encontrar um dígito que esteja apenas no início de uma linha.
string1 = "linea 1"string2 = "2ª linea"print(re.findall("^d", string1))print(re.findall("^d", string2))
[]['2']
Como você pode ver, há apenas um dígito no início da linha em string2
.
Da mesma forma, o final de uma linha pode ser encontrado com $
. Se quisermos encontrar um dígito apenas no final de uma linha
string1 = "linea 1"string2 = "2ª linea"print(re.findall("d$", string1))print(re.findall("d$", string2))
['1'][]
Isso só ocorre em string1
.
Exemplos práticos
Registros
Se no registro a seguir quisermos encontrar apenas os WARN
s
log = """[LOG ENTRY] [ERROR] The system is unstable[LOG ENTRY] [WARN] The system may be down[LOG ENTRY] [WARN] Microsoft just bought Github[LOG DATA] [LOG] Everything is OK[LOG ENTRY] [LOG] [user:@beco] Logged in[LOG ENTRY] [LOG] [user:@beco] Clicked here[LOG DATA] [LOG] [user:@celismx] Did something[LOG ENTRY] [LOG] [user:@beco] Rated the app[LOG ENTRY] [LOG] [user:@beco] Logged out[LOG LINE] [LOG] [user:@celismx] Logged in"""result = re.findall("[LOG.*[WARN].*", log)result
['[LOG ENTRY] [WARN] The system may be down','[LOG ENTRY] [WARN] Microsoft just bought Github']
Número de telefone
Dentro de um número, podemos encontrar letras como e
para ramal, #
também para ramal ou p
para fazer uma pausa se um computador ligar. Também podemos encontrar o +
para indicar um prefixo de país e separadores como espaços, -
, .
, .
, .
, .
, .
, .
, .
, .
, .
, .
, .
.
tel = """55565856-58-1156.58.1156.78-9865 09 8776y87r9845y78-5678.87 6578 54-56+52156581158-11-11#24655256048p12355256048e123"""result = re.findall("+?d{2,3}[^da-zA-Z\n]?d{2,3}[^da-zA-Z\n]?d{2,3}[#pe]?d*", tel)result
['555658','56-58-11','56.58.11','56.78-98','65 09 87','78.87 65','78 54-56','+521565811','58-11-11#246','55256048p123','55256048e123']
Vamos explicar
+?
: começando com o caractere+
e contendo zero ou umd{2,3}
: a ser seguido por 2 a 3 dígitos- Em seguida, pode haver zero ou um caractere que não seja um dígito, nem uma letra de
a
az
, nem uma letra deA
aZ
, nem uma quebra de linha. d{2,3}
: a ser seguido por 2 a 3 dígitos- Em seguida, pode haver zero ou um caractere que não seja um dígito, nem uma letra de
a
az
, nem uma letra deA
aZ
, nem uma quebra de linha. d{2,3}
: a ser seguido por 2 a 3 dígitos[#pe]?
: então pode haver zero ou um caractere#
, oup
, oue
.- Por fim, que haja zero ou todos os números.
URLs
urls = """url: https://www.instagram.com/p/BXB4zsUlW5Z/?taken-by=beco.mxurl: http://instagram.com/p/blablablahurl: http://itam.mx/testhttp://instagram.com/p/blablablahhttps://www.vanguarsoft.com.vehttp://platzi.comhttps://traetelo.nethttps://traetelo.net/images archivo.jspurl: https://subdominio.traetelo.neturl: https://www.instagram.com/p/BXB4zsUlW5Z/?taken-by=beco.mxurl: http://instagram.com/p/blablablahurl: http://itam.mx/testhttp://instagram.com/p/blablablahhttps://www.google.com.co/https://sub.dominio.de.alguien.com/archivo.htmlhttps://en.wikipedia.org/wiki/.orghttps://cdn-microsoft.org/image/seixo2t9sjl_22.jpghttps://hola.pizzahttps://platzi.com/clases/1301-expresiones-regulares/11860-urls9102/ clasehttps://api.giphy.com/v1/gifs/search?q=Rick and Morty&limit=10&api_key=DG3hItPp5HIRNC0nit3AOR7eQZAehttp://localhost:3000/something?color1=red&color2=bluehttp://localhost:3000/display/post?size=smallhttp://localhost:3000/?name=satyamhttp://localhost:3000/scanned?orderid=234http://localhost:3000/getUsers?userId=12354411&name=Billyhttp://localhost:3000/getUsers?userId=12354411http://localhost:3000/search?city=Barcelonawww.sitiodeejemplo.net/pagina.php?nombredevalor1=valor1&nombredevalor2=valor2"""result = re.findall("https?://[w-.]+.w{2,6}/?S*", urls)result
['https://www.instagram.com/p/BXB4zsUlW5Z/?taken-by=beco.mx','http://instagram.com/p/blablablah','http://itam.mx/test','http://instagram.com/p/blablablah','https://www.vanguarsoft.com.ve','http://platzi.com','https://traetelo.net','https://traetelo.net/images','https://subdominio.traetelo.net','https://www.instagram.com/p/BXB4zsUlW5Z/?taken-by=beco.mx','http://instagram.com/p/blablablah','http://itam.mx/test','http://instagram.com/p/blablablah','https://www.google.com.co/','https://sub.dominio.de.alguien.com/archivo.html','https://en.wikipedia.org/wiki/.org','https://cdn-microsoft.org/image/seixo2t9sjl_22.jpg','https://hola.pizza','https://platzi.com/clases/1301-expresiones-regulares/11860-urls9102/','https://api.giphy.com/v1/gifs/search?q=Rick']
Vamos explicar
http
: queremos que ele comece comhttp
.s?
: Então pode ou não haver ums
.:\/
: seguido por://
- `[+]: seguido de uma ou mais letras, traços ou pontos
- Então, um ponto
w{2,6}
: Entre 2 e 6 letras para o tld\/?
: seguido por zero ou um/
- Nenhum ou tudo que não seja um espaço.
E-mails
mails = """esto.es_un.mail@mail.comesto.es_un.mail+complejo@mail.comdominio.comrodrigo.jimenez@yahoo.com.mxruben@starbucks.comesto_no$es_email@dominio.comno_se_de_internet3@hotmail.com"""result = re.findall("[w._]{5,30}+?[w._]{0,10}@[w.-]{2,}.w{2,6}", mails)result
['esto.es_un.mail@mail.com','esto.es_un.mail+complejo@mail.com','rodrigo.jimenez@yahoo.com.mx','ruben@starbucks.com','es_email@dominio.com','no_se_de_internet3@hotmail.com']
Vamos explicar
- {5,30}`: queremos que ele comece com um número entre 5 e 30 (que é o mínimo e o máximo que o gmail suporta) letras, pontos ou sublinhados.
- Seguido por um zero ou um
+
. - {0,10}`: Então, entre 0 e 10 letras, pontos ou barras
@
: O@
: O@
: O@
: O@
[{2,}
: Entre 2 e infinitas letras, pontos e hífens (domínio)- seguido de um '`.
w{2,6}
: E, finalmente, entre 2 e 6 letras para o tld
Localizações
Há duas maneiras possíveis de fornecer locais, portanto, examinaremos ambas
loc = """-99.205646,19.429707,2275.10-99.205581, 19.429652,2275.10-99.204654,19.428952,2275.58"""result = re.findall("-?d{1,3}.d{1,6},s?-?d{1,3}.d{1,6},.*", loc)result
['-99.205646,19.429707,2275.10','-99.205581, 19.429652,2275.10','-99.204654,19.428952,2275.58']
Vamos explicar
- Queremos que ele comece com zero ou um sinal de menos.
- Seguido de um a três números
- Então, um ponto
d{1,6}
: Depois de um a seis números,
: Then a,
: Then a,
: Then a,
: Then a,
: Then a,
s?
: Após zero ou um espaço- ``-?`: Zero ou um sinal de menos
- ``: Então, entre um e três números
- Então, um ponto
- Seguido de um a seis números
,
: depois uma vírgula- Finalmente, nenhum ou todos os tipos de caracteres
loc = """-99 12' 34.08"W, 19 34' 56.98"N-34 54' 32.00"E, -3 21' 67.00"S"""result = re.findall("-?d{1,3}sd{1,2}'sd{1,2}.d{2,2}\"[WE],s?-?d{1,3}sd{1,2}'sd{1,2}.d{2,2}\"[SN]", loc)result
['-99 12' 34.08"W, 19 34' 56.98"N', '-34 54' 32.00"E, -3 21' 67.00"S']
print(result[0])print(result[1])
-99 12' 34.08"W, 19 34' 56.98"N-34 54' 32.00"E, -3 21' 67.00"S
Vamos explicar
- Queremos que ele comece com zero ou um sinal de menos.
- Seguido de um a três números
s
: Em seguida, um espaçod{1,2}
: Segmento de um a dois números'
: Then a'
: Then a'
: Then a'
: Then a'
: Then a'
- Seguido de um espaço
- ``: Então, entre um e dois números
- Após um ponto
d{2,2}
: seguido por dois números"
: then a"
: then a"
: then a"
: then a"
.[WE]
: Em seguida, a letraW
ou a letraE
.,
: Após uma vírgula- Seguido por um zero ou um espaço
- ``-?`: Então, zero ou um sinal de menos
- ``: Então, entre um e três números
- Seguido de um espaço
- ``: Então, entre um e dois números
'
: Then a'
: After a'
s
: Em seguida, um espaçod{1,2}`: Então, entre um e dois números
- seguido de um ponto final
d{2,2}
: Então, dois números"
: Seguido por"
.[SN]
: E, finalmente, a letraS
ou a letraN
.
Nomes
nombres = """Camilo Sarmiento GálvezAlejandro Pliego AbastoMilagros Reyes JapónSamuel París ArrabalJuan Pablo TafallaAxel Gálvez VelázquezÓscar Montreal AparicioJacobo Pozo TassisGuillermo Ordóñez EspigaEduardo Pousa CurbeloIvanna Bienvenida KevinAda Tasis LópezLuciana Sáenz GarcíaFlorencia Sainz MárquzCatarina Cazalla LombardaPaloma Gallo PerroMargarita Quesada FlorezVicente Fox QuesadaIris GracianiAsunción CarballarConstanza MuñozManuel Andres García Márquez"""result = re.findall("[A-ZÁÉÍÓÚ][a-záéíóú]+s[A-ZÁÉÍÓÚ][a-záéíóú]+s[A-ZÁÉÍÓÚ][a-záéíóú]+", nombres)result
['Camilo Sarmiento Gálvez','Alejandro Pliego Abasto','Milagros Reyes Japón','Samuel París Arrabal','Juan Pablo Tafalla','Axel Gálvez Velázquez','Óscar Montreal Aparicio','Jacobo Pozo Tassis','Espiga Eduardo Pousa','Curbelo Ivanna Bienvenida','Kevin Ada Tasis','López Luciana Sáenz','García Florencia Sainz','Márquz Catarina Cazalla','Lombarda Paloma Gallo','Perro Margarita Quesada','Florez Vicente Fox','Quesada Iris Graciani','Asunción Carballar Constanza','Manuel Andres García']
Vamos explicar
[A-ZÁÉÍÓÚ]
: queremos que ele comece com uma letra maiúscula, incluindo acentos.[a-záééíóú]+
: seguido de uma ou mais letras minúsculas, entre espaços- Seguido de um espaço
[A-ZÁÉÍÓÚ]
: seguido de uma letra maiúscula, incluindo acentos[a-záééíóú]+
: seguido de uma ou mais letras minúsculas, entre espaços- Seguido de um espaço
[A-ZÁÉÍÓÚ]
: seguido de uma letra maiúscula, incluindo acentos[a-záééíóú]+
: seguido de uma ou mais letras minúsculas, entre espaços
Pesquisar e substituir
Faremos o download de um arquivo com vários filmes históricos.
# download file from urlimport urllib.requesturl = "https://static.platzi.com/media/tmp/class-files/github/moviedemo/moviedemo-master/movies.dat"urllib.request.urlretrieve(url, "movies.dat")
---------------------------------------------------------------------------HTTPError Traceback (most recent call last)Cell In[43], line 42 import urllib.request3 url = "https://static.platzi.com/media/tmp/class-files/github/moviedemo/moviedemo-master/movies.dat"----> 4 urllib.request.urlretrieve(url, "movies.dat")File ~/miniconda3/envs/mybase/lib/python3.11/urllib/request.py:241, in urlretrieve(url, filename, reporthook, data)224 """225 Retrieve a URL into a temporary location on disk.226(...)237 data file as well as the resulting HTTPMessage object.238 """239 url_type, path = _splittype(url)--> 241 with contextlib.closing(urlopen(url, data)) as fp:242 headers = fp.info()244 # Just return the local path and the "headers" for file://245 # URLs. No sense in performing a copy unless requested.File ~/miniconda3/envs/mybase/lib/python3.11/urllib/request.py:216, in urlopen(url, data, timeout, cafile, capath, cadefault, context)214 else:215 opener = _opener--> 216 return opener.open(url, data, timeout)File ~/miniconda3/envs/mybase/lib/python3.11/urllib/request.py:525, in OpenerDirector.open(self, fullurl, data, timeout)523 for processor in self.process_response.get(protocol, []):524 meth = getattr(processor, meth_name)--> 525 response = meth(req, response)527 return responseFile ~/miniconda3/envs/mybase/lib/python3.11/urllib/request.py:634, in HTTPErrorProcessor.http_response(self, request, response)631 # According to RFC 2616, "2xx" code indicates that the client's632 # request was successfully received, understood, and accepted.633 if not (200 <= code < 300):--> 634 response = self.parent.error(635 'http', request, response, code, msg, hdrs)637 return responseFile ~/miniconda3/envs/mybase/lib/python3.11/urllib/request.py:563, in OpenerDirector.error(self, proto, *args)561 if http_err:562 args = (dict, 'default', 'http_error_default') + orig_args--> 563 return self._call_chain(*args)File ~/miniconda3/envs/mybase/lib/python3.11/urllib/request.py:496, in OpenerDirector._call_chain(self, chain, kind, meth_name, *args)494 for handler in handlers:495 func = getattr(handler, meth_name)--> 496 result = func(*args)497 if result is not None:498 return resultFile ~/miniconda3/envs/mybase/lib/python3.11/urllib/request.py:643, in HTTPDefaultErrorHandler.http_error_default(self, req, fp, code, msg, hdrs)642 def http_error_default(self, req, fp, code, msg, hdrs):--> 643 raise HTTPError(req.full_url, code, msg, hdrs, fp)HTTPError: HTTP Error 403: Forbidden
Vamos imprimir as primeiras 10 linhas para analisá-las.
file = open("movies.dat", "r")for i, line in enumerate(file):print(line, end="")if i == 10:breakfile.close()
1::Toy Story (1995)::Adventure|Animation|Children|Comedy|Fantasy2::Jumanji (1995)::Adventure|Children|Fantasy3::Grumpier Old Men (1995)::Comedy|Romance4::Waiting to Exhale (1995)::Comedy|Drama|Romance5::Father of the Bride Part II (1995)::Comedy6::Heat (1995)::Action|Crime|Thriller7::Sabrina (1995)::Comedy|Romance8::Tom and Huck (1995)::Adventure|Children9::Sudden Death (1995)::Action10::GoldenEye (1995)::Action|Adventure|Thriller11::American President, The (1995)::Comedy|Drama|Romance
Como você pode ver, temos um ID, seguido por ::
, depois o nome do filme, entre parênteses o ano, seguido por ::
e depois os gêneros separados por |
.
Podemos fazer uma limpeza de arquivo muito fácil usando expressões regulares, as funções compile
e match
e o uso de agrupamento com parênteses. Ao agrupar, selecionamos as áreas do texto que queremos manter e, em seguida, trabalhamos com elas como quisermos.
pattern = re.compile(r"^d+::([ws:,().-'&¡!/¿?ÁÉÍÓÚáéíóú+*$#°'"[]@·]+)s((d{4,4}))::(.*)$")file = open("movies.dat", "r")file_filtered = open("movies.csv", "w")file_filtered.write("title,year,genders ")sep = ";;"for line in file:result = re.match(pattern, line)if result:file_filtered.write(f"{result.group(1)}{sep}{result.group(2)}{sep}{result.group(3)} ")else:print(line, end="")file.close()file_filtered.close()
Vejamos o que fizemos: primeiro, definimos um padrão com o seguinte:
^
: Queremos que ele comece com o início da linha.- Próximo um ou mais números
::
: Seguido por::
- `((([\¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡: esse é o primeiro agrupamento, procuramos qualquer palavra, espaço ou caractere entre colchetes que apareça uma ou mais vezes.
s
: depois um espaço- `: O fechamento de um parêntese
- (4,4})`: Aqui está o segundo agrupamento, estamos procurando por quatro números.
- Após o fechamento de um parêntese
::
: Then::
(.*)
: o terceiro agrupamento, qualquer caractere que não ocorra nenhuma ou todas as vezes$
: Por último, o final da linha
Dentro do for
, analisamos linha por linha se o padrão que definimos foi encontrado e, se for encontrado, escrevemos os três padrões no csv
separados por sep
, que, no nosso caso, definimos como ;;
. Esse separador foi definido porque há títulos de filmes que têm ,
s.
Lemos o csv
com o Pandas
.
import pandas as pd
df = pd.read_csv("movies.csv", sep=";;", engine="python")
df.head()
Folha de dicas
Aqui está um [cheatsheet] (https://cheatography.com/davechild/cheat-sheets/regular-expressions/pdf/) com vários padrões