| You are here: Inicio > Inmersión en Python > Expresiones regulares > Caso de estudio: análisis de números de teléfono | << >> | ||||
Inmersión en PythonPython de novato a experto |
|||||
Por ahora se ha concentrado en patrones completos. Cada patrón coincide, o no. Pero las expresiones regulares son mucho más potentes que eso. Cuando una expresión regular coincide, puede extraer partes concretas. Puede saber qué es lo que causó la coincidencia.
Este ejemplo sale de otro problema que he encontrado en el mundo real, de nuevo de un empleo anterior. El problema: analizar un número de teléfono norteamericano. El cliente quería ser capaz de introducir un número de forma libre (en un único campo), pero quería almacenar por separado el código de área, la troncal, el número y una extensión opcional en la base de datos de la compañía. Rastreando la Web encontré muchos ejemplo de expresiones regulares que supuestamente conseguían esto, pero ninguna era lo suficientemente permisiva.
Éstos son los números de teléfono que había de poder aceptar:
¡Qué gran variedad! En cada uno de estos casos, necesitaba saber que el código de área era 800, la troncal 555, y el resto del número de teléfono era 1212. Para aquellos con extensión, necesitaba saber que ésta era 1234.
Vamos a desarrollar una solución para el análisis de números de teléfono. Este ejemplo le muestra el primer paso.
>>> phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})$')>>> phonePattern.search('800-555-1212').groups()
('800', '555', '1212') >>> phonePattern.search('800-555-1212-1234')
>>>
| Lea siempre una expresión regular de izquierda a derecha. Ésta coincide con el comienzo de la cadena, y luego (\d{3}). ¿Qué es \d{3}? Bien, el {3} significa “coincidir con exactamente tres caracteres”; es una variante de la sintaxis {n,m} que vimos antes. \d significa “un dígito numérico” (de 0 a 9). Ponerlo entre paréntesis indica “coincide exactamente con tres dígitos numéricos y recuérdalos como un grupo que luego te los voy a pedir”. Luego coincide con un guión. Después con otro grupo de exactamente tres dígitos. Luego con otro guión. Entonces con otro grupo de exactamente cuatro dígitos. Y ahora con el final de la cadena. | |
| Para acceder a los grupos que ha almacenado el analizador de expresiones por el camino, utilice el método groups() del objeto que devuelve la función search. Obtendrá una tupla de cuantos grupos haya definido en la expresión regular. En este caso, hemos definido tres grupos, un con tres dígitos, otro con tres dígitos, y otro más con cuatro dígitos. | |
| Esta expresión regular no es la respuesta final, porque no trabaja con un número de teléfono con extensión al final. Para eso, hace falta aumentar la expresión. |
>>> phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})-(\d+)$')>>> phonePattern.search('800-555-1212-1234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('800 555 1212 1234')
>>> >>> phonePattern.search('800-555-1212')
>>>
El siguiente ejemplo muestra la expresión regular que maneja separadores entre diferentes partes del número de teléfono.
>>> phonePattern = re.compile(r'^(\d{3})\D+(\d{3})\D+(\d{4})\D+(\d+)$')>>> phonePattern.search('800 555 1212 1234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212-1234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('80055512121234')
>>> >>> phonePattern.search('800-555-1212')
>>>
El siguiente ejemplo muestra la expresión regular para manejar números de teléfonos sin separadores.
>>> phonePattern = re.compile(r'^(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')>>> phonePattern.search('80055512121234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('800.555.1212 x1234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212').groups()
('800', '555', '1212', '') >>> phonePattern.search('(800)5551212 x1234')
>>>
El siguiente ejemplo muestra cómo manejar los caracteres antes del número de teléfono.
>>> phonePattern = re.compile(r'^\D*(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')>>> phonePattern.search('(800)5551212 ext. 1234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212').groups()
('800', '555', '1212', '') >>> phonePattern.search('work 1-(800) 555.1212 #1234')
>>>
Parémonos a pensar por un momento. La expresión regular hasta ahora ha buscado coincidencias partiendo siempre del inicio de la cadena. Pero ahora vemos que hay una cantidad indeterminada de cosas al principio de la cadena que queremos ignorar. En lugar de intentar ajustarlo todo para simplemente ignorarlo, tomemos un enfoque diferente: no vamos a buscar coincidencias explícitamente desde el principio de la cadena. Esto lo mostramos en el siguiente ejemplo.
>>> phonePattern = re.compile(r'(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')>>> phonePattern.search('work 1-(800) 555.1212 #1234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212')
('800', '555', '1212', '') >>> phonePattern.search('80055512121234')
('800', '555', '1212', '1234')
¿Ve lo rápido que pueden descontrolarse las expresiones regulares? Eche un vistazo rápido a cualquiera de las iteraciones anteriores. ¿Podría decir la diferencia entre ésa y la siguiente?
Aunque entienda la respuesta final (y es la definitiva; si ha descubierto un caso que no se ajuste, yo no quiero saber nada), escribámoslo como una expresión regular prolija, antes de que olvidemos por qué hicimos cada elección.
>>> phonePattern = re.compile(r''' # don't match beginning of string, number can start anywhere (\d{3}) # area code is 3 digits (e.g. '800') \D* # optional separator is any number of non-digits (\d{3}) # trunk is 3 digits (e.g. '555') \D* # optional separator (\d{4}) # rest of number is 4 digits (e.g. '1212') \D* # optional separator (\d*) # extension is optional and can be any number of digits $ # end of string ''', re.VERBOSE) >>> phonePattern.search('work 1-(800) 555.1212 #1234').groups()('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212')
('800', '555', '1212', '')
<< Expresiones regulares prolijas |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
Resumen >> |