Regular expressions

Regular expressions Regular expressions

Expresiones regulareslink image 0

	
import re
Copy

Métodoslink image 1

Findalllink image 2

Con el método findall() podemos encontrar todas las coincidencias de un patrón en un string

	
import re
string = "Hola, soy un string"
print(re.findall("Hola, soy", string))
Copy
	
['Hola, soy']

Pero si queremos encontrar la posición donde se encuentra un patrón, podemos usar el método search() para buscar un patrón en un string. Este método devuelve un objeto de tipo Match si encuentra una coincidencia, y si no devuelve None.

	
print(re.search("soy", string))
Copy
	
<re.Match object; span=(6, 9), match='soy'>

Matchlink image 4

También podemos usar el método match() que busca el patrón al principio del string.

	
print(re.match("Hola", string))
print(re.match("soy", string))
Copy
	
<re.Match object; span=(0, 4), match='Hola'>
None

Spanlink image 5

Si queremos obtener la posición de la coincidencia, podemos usar el método span() que devuelve una tupla con la posición inicial y final de la coincidencia.

	
print(re.match("Hola", string).span())
Copy
	
(0, 4)

Grouplink image 6

Sabiendo la posición de la coincidencia, podemos usar el método group() para obtener la subcadena que coincide con el patrón.

	
print(re.match("Hola", string).group())
Copy
	
Hola

También podríamos usar el inicio y el final de la coincidencia para hacer un slice del string.

	
start, end = re.match("Hola", string).span()
print(string[start:end])
Copy
	
Hola

Splitlink image 7

Con el método split() podemos dividir un string en una lista de subcadenas usando un patrón como separador.

	
split = re.split("soy", string)
print(split)
Copy
	
['Hola, ', ' un string']

Se ha dividido la frase en dos strings usando "soy" como separador.

Sublink image 8

Con el método sub() podemos reemplazar todas las coincidencias de un patrón por otra subcadena.

	
sub = re.sub("soy", "eres", string)
print(sub)
Copy
	
Hola, eres un string

Ha reemplazado todas las coincidencias de "soy" por "eres".

Patroneslink image 9

El caracter .link image 10

Con el caracter . podemos buscar cualquier caracter, cualquier caracter que haya en nuestro string será encontrado

	
string = "Hola, soy un string"
print(re.findall(".", string))
Copy
	
['H', 'o', 'l', 'a', ',', ' ', 's', 'o', 'y', ' ', 'u', 'n', ' ', 's', 't', 'r', 'i', 'n', 'g']

Si por ejemplo queremos secuencias de dos caracteres buscaríamos con dos .s seguidos

	
string1 = "Hola, soy un string"
string2 = "Hola, soy un string2"
print(re.findall("..", string1))
print(re.findall("..", string2))
Copy
	
['Ho', 'la', ', ', 'so', 'y ', 'un', ' s', 'tr', 'in']
['Ho', 'la', ', ', 'so', 'y ', 'un', ' s', 'tr', 'in', 'g2']

Como podemos ver string1 tiene un numero impar de caracteres, por lo que la última g no la coge, si embargo string2 tiene un número par de caracteres, por lo que coge todos los caracteres

Vamos a ver esto de otra forma, vamos a cambiar cada secuencia de tres caracteres por un símbolo de $

	
print(string1)
print(re.sub("...", "$ ", string1))
Copy
	
Hola, soy un string
$ $ $ $ $ $ g

He impreso dos espacios después de cada $ para que se vea el cambio, se puede ver como el último caracter no lo convierte

Las clases predefinidas y construidaslink image 11

Dígitolink image 12

Si queremos encontrar los dígitos necesitamos usar \d

	
string = "Hola, soy un string con 123 digitos"
print(re.findall("d", string))
Copy
	
['1', '2', '3']

Al igual que antes, si por ejemplo queremos dos dígitos, ponemos \d dos veces

	
print(re.findall("dd", string))
Copy
	
['12']

Letralink image 13

Si queremos encontrar letras necesitamos usar \w. Se entiende por word todas las letras de la a a la z, de la A a la Z, los números de 0 al 9 y el _

	
string = "Hola, soy un_string con, 123 digitos"
print(re.findall("w", string))
Copy
	
['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 vemos coge todo menos los espacios y la coma

Espacioslink image 14

Si queremos encontrar espacios necesitamos \s

	
string = "Hola, soy un_string con, 123 digitos"
print(re.sub("s", "*", string))
Copy
	
Hola,*soy*un_string*con,*123*digitos

Las expresiones regulares considera los saltos de línea como espacios

	
string = """Hola, soy un string
con un salto de línea"""
print(re.sub("s", "*", string))
Copy
	
Hola,*soy*un*string**con*un*salto*de*línea

Rangoslink image 15

Si queremos buscar un rango usamos [], por ejemplo, si queremos los números del 4 al 8 usamos

	
string = "1234567890"
print(re.findall("[4-8]", string))
Copy
	
['4', '5', '6', '7', '8']

Podemos ampliar el rango de búsqueda

	
string = "1234567890"
print(re.findall("[2-57-9]", string))
Copy
	
['2', '3', '4', '5', '7', '8', '9']

Si además queremos encontrar un caracter en concreto ponemos el carcacter seguido de \

	
string = "1234567890."
print(re.findall("[2-57-9.]", string))
Copy
	
['2', '3', '4', '5', '7', '8', '9', '.']

Corchete [ y corchete ]link image 16

Como hemos visto, si queremos encontrar rangos usamos [], pero ¿qué pasa si queremos encontrar solo el [ o el ]? Para ello tenemos que usar \[ y \]

	
string = "[1234567890]"
print(re.findall("[", string))
print(re.findall("]", string))
Copy
	
['[']
[']']

Los delimitadores +, *, ?link image 17

Star * (ninguno o todos)link image 18

Con el delimitador * se indica que quieres que te busque ninguno o todos, no uno a uno como antes

	
string = "Hola, soy un string con 12 123 digitos"
print(re.findall("d", string))
print(re.findall("d*", string))
Copy
	
['1', '2', '1', '2', '3']
['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '12', '', '123', '', '', '', '', '', '', '', '', '']

Como se puede ver, al poner el * ha encontrado todas las posiciones en las que hay cero caracteres o todos los caracteres

Plus + (uno o más)link image 19

Con el delimitador + se indica que quieres que te busque uno o más

	
string = "Hola, soy un string con 1 12 123 digitos"
print(re.findall("d+", string))
Copy
	
['1', '12', '123']

Optional ? (cero o uno)link image 20

Con el delimitador ? se indica que quieres que te busque cero o uno

	
string = "Hola, soy un string con 1 12 123 digitos"
print(re.sub("d?", "-", string))
Copy
	
-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-

Contadoreslink image 21

Cuando queremos encontrar algo que aparezca x veces usamos los contadores mediante las llaves {}. Por ejemplo, si queremos encontrar una secuencia en la que al menos haya dos dígitos

	
string = "Hola, soy un string con 1 12 123 1234 1234digitos"
print(re.findall("d{2}", string))
Copy
	
['12', '12', '12', '34', '12', '34']

Como se puede ver ha encontrado las secuencias 12 y 34

Los contadores aceptan una cota superior e inferior {inf, sup}

	
string = "Hola, soy un string con 1 12 123 1234 1234digitos"
print(re.findall("d{2,5}", string))
Copy
	
['12', '123', '1234', '1234']

Si no se define la cota superior, significa que se quiere como mínimo la cantidad de elementos que se ha indicado, pero sin límite superior

	
string = "Hola, soy un string con 1 12 123 1234 12345464168415641646451563416 digitos"
print(re.findall("d{2,}", string))
Copy
	
['12', '123', '1234', '12345464168415641646451563416']

Si queremos usar la notación de cota superior e inferior, pero queremos un número fijo, se tiene que poner dicho número en las dos cotas

	
string = "Hola, soy un string con 1 12 123 1234 12345464168415641646451563416 digitos"
print(re.findall("d{2,3}", string))
Copy
	
['12', '123', '123', '123', '454', '641', '684', '156', '416', '464', '515', '634', '16']

Claseslink image 22

Se pueden crear clases mediante corchetes []. En realidad vimos que este servía para los rangos, pero, una vez que se define lo que se quiere que haya dentro, se puede considerar como una clase y operar con el

Por ejemplo, supongamos que tenemos un número de teléfono, que puede darse de las siguientes maneras

  • 666-66-66-66
  • 666-666-666
  • 666 666 666
  • 666 66 66 66
  • 666666666

Hay muchas maneras de dar un número, así que vamos a ver cómo crear una clase para definir el delimitador

Primero vamos a decir que busque todas las secuencias de números en las que haya como mínimo dos 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))
Copy
	
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']

Ahora definimos que encuentre el separador como un - o un espacio

	
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))
Copy
	
string1: 666-66-66-66 --> ['-', '-', '-']
string2: 666-666-666 --> ['-', '-']
string3: 666 66 66 66 --> [' ', ' ', ' ']
string4: 666 666 666 --> [' ', ' ']
string5: 666666666 --> []

Como se ve en el último string no ha encontrado, por lo que añadimos un ? para que encuentre cuando haya cero o uno

	
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))
Copy
	
string1: 666-66-66-66 --> ['', '', '', '-', '', '', '-', '', '', '-', '', '', '']
string2: 666-666-666 --> ['', '', '', '-', '', '', '', '-', '', '', '', '']
string3: 666 66 66 66 --> ['', '', '', ' ', '', '', ' ', '', '', ' ', '', '', '']
string4: 666 666 666 --> ['', '', '', ' ', '', '', '', ' ', '', '', '', '']
string5: 666666666 --> ['', '', '', '', '', '', '', '', '', '']

Ahora buscamos que esté todo 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))
Copy
	
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 vemos en el string2 y string4, no encuentra nada. Hemos puesto el filtro \d{2,}[\-\s]? 4 veces, es decir queremos una secuencia de al menos dos números, seguido de cero o un separador de tipo guion o espacio que se repita 4 veces. Pero en la última secuencia no hace falta el [\-\s]?, ya que nunca va a terminar un número con un espacio o un guión

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

Sigue sin encontrar para string2 y string4. Esto es porque lo último que hay en el filtro es un d{2,}, es decir, después del tercer separador estamos esperando al menos 2 números, pero eso en string2 y string4 no pasa, así que ponemos lo siguiente

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

El delimitador ? como delimitador rápidolink image 23

El ejemplo anterior lo podemos filtrar mediante \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))
Copy
	
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 --> []

Si no estuviese el delimitador ? tendríamos \d+[- ], lo que quiere decir una secuencia de uno o más números seguidos de un espacio o un guión. Pero lo que hace el delimitador ? es hacer esta búsqueda más rápido

El negadorlink image 24

Antes hemos visto que con \d encotrábamos dígitos, pues con \D encontramos todo lo que no sean dígitos

	
string1 = "E3s4t6e e1s2t3r5i6n7g8 t9i0e4n2e1 d4i5g7i9t0o5s2"
print(re.findall("D", string1))
Copy
	
['E', 's', 't', 'e', ' ', 'e', 's', 't', 'r', 'i', 'n', 'g', ' ', 't', 'i', 'e', 'n', 'e', ' ', 'd', 'i', 'g', 'i', 't', 'o', 's']

Lo mismo ocurre con las letras, si escribimos \W encontrará todo lo que no sean letras

	
string1 = "Letras ab27_ no letras ,.:;´ç"
print(re.findall("W", string1))
Copy
	
[' ', ' ', ' ', ' ', ',', '.', ':', ';', '´']

Si ponemos \S encontraremos todo lo que no sean espacios

	
print(re.findall("S", string1))
Copy
	
['L', 'e', 't', 'r', 'a', 's', 'a', 'b', '2', '7', '_', 'n', 'o', 'l', 'e', 't', 'r', 'a', 's', ',', '.', ':', ';', '´', 'ç']

Pero en caso que tengamos una clase o quialquier otra cosa, podemos negar mediante ^

	
string1 = "1234567890"
print(re.findall("[^5-9]", string1))
Copy
	
['1', '2', '3', '4', '0']

Volviendo al ejemplo de los números de teléfono de antes, podemos filtrarlos mediante los siguiente

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

Lo que estamos haciendo es pedir secuencias de al menos dos dígitos seguido de uno o ningún no dígito

El principio ^ y final de línea $link image 25

Con ^ podemos buscar el inicio de línea, por ejemplo, si queremos encontrar un dígito que solo esté al inicio de una línea

	
string1 = "linea 1"
string2 = "2ª linea"
print(re.findall("^d", string1))
print(re.findall("^d", string2))
Copy
	
[]
['2']

Como se puede ver solo hay un dígito al inicio de linea en string2

Al igual, el final de línea se puede encontrar con $. Si queremos encontrar un dígito solo al final de una línea

	
string1 = "linea 1"
string2 = "2ª linea"
print(re.findall("d$", string1))
print(re.findall("d$", string2))
Copy
	
['1']
[]

Esto solo ocurre en el string1

Ejemplos prácticoslink image 26

Logslink image 27

Si en el siguiente log queremos encontrar solo los WARNs

	
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
Copy
	
['[LOG ENTRY] [WARN] The system may be down',
'[LOG ENTRY] [WARN] Microsoft just bought Github']

Número de teléfonolink image 28

Dentro de un número podemos encontrarnos letras como la e de extensión, # también para la extensión, o la p para que si llama un ordenador haga una pausa. También podemos encontrar el + para indicar un prefijo de pais y separadores como espacios, -, .

	
tel = """555658
56-58-11
56.58.11
56.78-98
65 09 87
76y87r98
45y78-56
78.87 65
78 54-56
+521565811
58-11-11#246
55256048p123
55256048e123"""
result = re.findall("+?d{2,3}[^da-zA-Z\n]?d{2,3}[^da-zA-Z\n]?d{2,3}[#pe]?d*", tel)
result
Copy
	
['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']

Pasamos a explicarlo

  • \+?: Que empiece con el caracter + y que haya cero o uno
  • \d{2,3}: Que siga con entre 2 y 3 dígitos
  • [^\da-zA-Z\n]?: A continuación puede haber cero o un caracter que no sea ni un dígito, ni una letra de la a la z, ni una letra de la A a la Z, ni un salto de línea
  • \d{2,3}: Que siga con entre 2 y 3 dígitos
  • [^\da-zA-Z\n]?: A continuación puede haber cero o un caracter que no sea ni un dígito, ni una letra de la a la z, ni una letra de la A a la Z, ni un salto de línea
  • \d{2,3}: Que siga con entre 2 y 3 dígitos
  • [#pe]?: A continuación puede haber cero o un caracter tanto #, como p, como e
  • \d*: Por último que haya cero o todos los números

URLslink image 29

	
urls = """url: https://www.instagram.com/p/BXB4zsUlW5Z/?taken-by=beco.mx
url: http://instagram.com/p/blablablah
url: http://itam.mx/test
http://instagram.com/p/blablablah
https://www.vanguarsoft.com.ve
http://platzi.com
https://traetelo.net
https://traetelo.net/images archivo.jsp
url: https://subdominio.traetelo.net
url: https://www.instagram.com/p/BXB4zsUlW5Z/?taken-by=beco.mx
url: http://instagram.com/p/blablablah
url: 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/ clase
https://api.giphy.com/v1/gifs/search?q=Rick and Morty&limit=10&api_key=DG3hItPp5HIRNC0nit3AOR7eQZAe
http://localhost:3000/something?color1=red&color2=blue
http://localhost:3000/display/post?size=small
http://localhost:3000/?name=satyam
http://localhost:3000/scanned?orderid=234
http://localhost:3000/getUsers?userId=12354411&name=Billy
http://localhost:3000/getUsers?userId=12354411
http://localhost:3000/search?city=Barcelona
www.sitiodeejemplo.net/pagina.php?nombredevalor1=valor1&nombredevalor2=valor2"""
result = re.findall("https?://[w-.]+.w{2,6}/?S*", urls)
result
Copy
	
['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']

Pasamos a explicarlo

  • http: Queremos que empiece por http
  • s?: A continuación puede haber o no una s
  • :\/\/: Seguido de ://
  • [\w\-\.]+: Seguido de uno o más letras, giones o puntos
  • \.: A continuación un punto
  • \w{2,6}: Entre 2 y 6 letras para el tld
  • \/?: Seguido de cero o un /
  • \S*: Ninguno o todo lo que no sea un espacio

Mailslink image 30

	
mails = """esto.es_un.mail@mail.com
esto.es_un.mail+complejo@mail.com
dominio.com
rodrigo.jimenez@yahoo.com.mx
ruben@starbucks.com
esto_no$es_email@dominio.com
no_se_de_internet3@hotmail.com"""
result = re.findall("[w._]{5,30}+?[w._]{0,10}@[w.-]{2,}.w{2,6}", mails)
result
Copy
	
['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']

Pasamos a explicarlo

  • [\w\._]{5,30}: Queremos que empiece por entre 5 y 30 (que es lo mínimo y máximo que admite gmail) letras, puntos o barras bajas
  • \+?: Seguido de cero o un +
  • [\w\._]{0,10}: A continuación entre 0 y 10 letras, puntos o barras bajas
  • @: La @
  • [\w\.-]{2,}: Entre 2 e infinitas letras, puntos y guiones (dominio)
  • \.: Seguido de un .
  • \w{2,6}: Y por último entre 2 y 6 letras para el tld

Localizacioneslink image 31

Hay dos posibles manera de dar localizaciones, por lo que analizamos las dos

	
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
Copy
	
['-99.205646,19.429707,2275.10',
'-99.205581, 19.429652,2275.10',
'-99.204654,19.428952,2275.58']

Pasamos a explicarlo

  • \-?: Queremos que empiece con cero o un signo menos
  • \d{1,3}: Seguido de entre uno y tres números
  • \.: A continuación un punto
  • \d{1,6}: Después entre uno y seis números
  • ,: A continuación una ,
  • \s?: Después cero o un espacio
  • \-?: Cero o un signo menos
  • \d{1,3}: A continuación entre uno y tres números
  • \.: A continuación un punto
  • \d{1,6}: Seguido de entre uno y seis números
  • ,: Luego una coma
  • .*: Por último ninguno o todo tipo 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
Copy
	
['-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])
Copy
	
-99 12' 34.08"W, 19 34' 56.98"N
-34 54' 32.00"E, -3 21' 67.00"S

Pasamos a explicarlo

  • \-?: Queremos que empiece con cero o un signo menos
  • \d{1,3}: Seguido de entre uno y tres números
  • \s: Después un espacio
  • \d{1,2}: Segiodo de entre uno y dos números
  • ': A continuación un '
  • \s: Seguido de un espacio
  • \d{1,2}: A continuación entre uno y dos números
  • \.: Después un punto
  • \d{2,2}: Seguido de dos números
  • \": Después un "
  • [WE]: A continuación la letra W o la letra E
  • ,: Después una coma
  • \s?: Seguido de cero o un espacio
  • \-?: Después cero o un signo menos
  • \d{1,3}: A continuación entre uno y tres números
  • \s: Seguido de un espacio
  • \d{1,2}: A continuación entre uno y dos números
  • ': Después un '
  • \s: Después un espacio
  • \d{1,2}: A continuación entre uno y dos númeors
  • \.: Seguido de un punto
  • \d{2,2}: Después dos números
  • \": Segido de "
  • [SN]: Y por último la letra S o la letra N

Nombreslink image 32

	
nombres = """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
Guillermo Ordóñez 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 Muñoz
Manuel Andres García Márquez"""
result = re.findall("[A-ZÁÉÍÓÚ][a-záéíóú]+s[A-ZÁÉÍÓÚ][a-záéíóú]+s[A-ZÁÉÍÓÚ][a-záéíóú]+", nombres)
result
Copy
	
['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']

Pasamos a explicarlo

  • [A-ZÁÉÍÓÚ]: Queremos que empiece con una letra mayúscula, incluidas con acentos
  • [a-záéíóú]+: Seguido de una o mas letras minúsculas, incluidas con espacios
  • \s: Segido de un espacio
  • [A-ZÁÉÍÓÚ]: A continuación una letra mayúscula, incluidas con acentos
  • [a-záéíóú]+: Seguido de una o mas letras minúsculas, incluidas con espacios
  • \s: Segido de un espacio
  • [A-ZÁÉÍÓÚ]: A continuación una letra mayúscula, incluidas con acentos
  • [a-záéíóú]+: Seguido de una o mas letras minúsculas, incluidas con espacios

Búsqueda y remplazolink image 33

Vamos a descargarnos un archivo con un montón de películas históricas

	
# download file from url
import urllib.request
url = "https://static.platzi.com/media/tmp/class-files/github/moviedemo/moviedemo-master/movies.dat"
urllib.request.urlretrieve(url, "movies.dat")
Copy
	
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
Cell In[43], line 4
2 import urllib.request
3 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 response
File ~/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's
632 # 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 response
File ~/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 result
File ~/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 a imprimir las primeras 10 lineas para analizarlo

	
file = open("movies.dat", "r")
for i, line in enumerate(file):
print(line, end="")
if i == 10:
break
file.close()
Copy
	
1::Toy Story (1995)::Adventure|Animation|Children|Comedy|Fantasy
2::Jumanji (1995)::Adventure|Children|Fantasy
3::Grumpier Old Men (1995)::Comedy|Romance
4::Waiting to Exhale (1995)::Comedy|Drama|Romance
5::Father of the Bride Part II (1995)::Comedy
6::Heat (1995)::Action|Crime|Thriller
7::Sabrina (1995)::Comedy|Romance
8::Tom and Huck (1995)::Adventure|Children
9::Sudden Death (1995)::Action
10::GoldenEye (1995)::Action|Adventure|Thriller
11::American President, The (1995)::Comedy|Drama|Romance

Como se puede ver, tenemos un ID, seguido de ::, a continuación el nombre de la película, entre paréntesis el año, sguido de :: y después géneros separados por |

Podemos hacer una limpieza del archivo muy fácil mediante las expresiones regulares, las funciones compile y match y el uso de agrupaciones con paréntesis. Al hacer agurpaciones, seleccionamos qué zonas del texto queremos guardar para luego trabajar con ellas como queramos, vamos e verlo con un ejemplo

	
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()
Copy

Vamos a ver qué hemos hecho, primero hemos definido un patrón con lo siguiente:

  • ^: Queremos que empiece con el inicio de línea
  • \d+: A continuación uno o más números
  • ::: Seguido de ::
  • ([\w\s:,\(\)\.\-'&¡!/¿?ÁÉÍÓÚáéíóú\+*\$#°\'\"\[\]@·]+): Esta es la primera agrupación, buscamos cualquier palabra, espacio o caracter de entre los corchetes que aparezca una o más veces
  • \s: A continuación un espacio
  • \(: La apretura de un paréntesis
  • (\d{4,4}): Aquí esta la segunda agrupación, buscamos cuatro números
  • \): Después el cierre de un paréntesis
  • ::: A continuación ::
  • (.*): La tercera agrupación, cualquier caracter que aparezca ninguna o todas las veces
  • $: Por último el final de línea

Dentro del for analizamos línea a línea si se encuentra el patrón que hemos definido, y si se encuentra se escriben los tres patrones en el csv separados por sep, que en nuestro caso lo hemos definido como ;;. Se ha definido ese separador, porque hay títulos de películas que tienen ,s.

Leemos el csv con Pandas

import pandas as pd
      df = pd.read_csv("movies.csv", sep=";;", engine="python")
      df.head()
      
Out[ ]:
title,year,genders
Toy Story 1995 Adventure|Animation|Children|Comedy|Fantasy
Jumanji 1995 Adventure|Children|Fantasy
Grumpier Old Men 1995 Comedy|Romance
Waiting to Exhale 1995 Comedy|Drama|Romance
Father of the Bride Part II 1995 Comedy

Cheatsheetlink image 34

Aquí tienes un cheatsheet con un montón de patrones

davechild_regular-expressions

Seguir leyendo

DoLa – Decoding by Contrasting Layers Improves Factuality in Large Language Models

DoLa – Decoding by Contrasting Layers Improves Factuality in Large Language Models

¿Alguna vez has hablado con un LLM y te ha respondido algo que suena como si hubiera estado bebiendo café de máquina durante toda la noche? 😂 ¡Eso es lo que llamamos una alucinación en el mundo de los LLMs! Pero no te preocupes, porque no es que tu modelo de lenguaje esté loco (aunque a veces puede parecerlo 🤪). La verdad es que los LLMs pueden ser un poco... creativos cuando se trata de generar texto. Pero gracias a DoLa, un método que utiliza capas de contraste para mejorar la factibilidad de los LLMs, podemos evitar que nuestros modelos de lenguaje se conviertan en escritores de ciencia ficción 😂. En este post, te explicaré cómo funciona DoLa y te mostraré un ejemplo de código para que puedas entender mejor cómo hacer que tus LLMs sean más fiables y menos propensos a inventar historias. ¡Vamos a salvar a nuestros LLMs de la locura y hacer que sean más útiles! 🚀

Últimos posts -->

¿Has visto estos proyectos?

Subtify

Subtify Subtify

Generador de subtítulos para videos en el idioma que desees. Además a cada persona le pone su subtítulo de un color

Ver todos los proyectos -->

¿Quieres aplicar la IA en tu proyecto? Contactame!

¿Quieres mejorar con estos tips?

Últimos tips -->

Usa esto en local

Los espacios de Hugging Face nos permite ejecutar modelos con demos muy sencillas, pero ¿qué pasa si la demo se rompe? O si el usuario la elimina? Por ello he creado contenedores docker con algunos espacios interesantes, para poder usarlos de manera local, pase lo que pase. De hecho, es posible que si pinchas en alún botón de ver proyecto te lleve a un espacio que no funciona.

Ver todos los contenedores -->

¿Quieres aplicar la IA en tu proyecto? Contactame!

¿Quieres entrenar tu modelo con estos datasets?

short-jokes-dataset

Dataset de chistes en inglés

opus100

Dataset con traducciones de inglés a español

netflix_titles

Dataset con películas y series de Netflix

Ver más datasets -->