Les cuento mi problema y la solución que apliqué con Python y Expresiones Regulares. Recibí un archivo en texto plano que incluye mayormente fechas en el formato conocido como timestamp que es de la forma aaaa-mm-dd hh:nn:ss. Este archivo lo subo a una base de datos SQL. Realmente no importa que base de datos, ya que todas se comportan de una manera más o menos parecida: MSSQL Express, MS Access, MySQL, PostgreSQL o SQLite3.
El problema es que en esta ocasión, las fechas venían con otro formato, uno como este mm/dd/aaaa hh:nn:ss, y por lo tanto no lo podía interpretar el motor de base de datos. Intenté primero configurar el motor para que aceptara esta nueva configuración, pero al no lograrlo, decidí aceptar el reto de cambiarla usando Python.
Era un verdadero reto, porque el archivo de 50 Mb contiene unas 179,000 líneas con 25 campos cada una con fechas. Esto significaba una rejilla de 4’475,000 fechas que tenían que ser reemplazadas.
Lo primero que hice fue buscar un sitio para probar la expresión regular (regular expression o simplemente regex) que utilizaría para el reemplazo. La página que utilizo para estas pruebas es RegEx Planet, que de una forma visual y sencilla prueba tus expresiones para que te asegures que funcionan.
Una línea, de las 179 mil, era algo como esto:
### Control|Texto Adicional||01/03/2011 08:48:47|01/03/2011 08:57:25|01/03/2011 10:45:38|||||||||01/03/2011 10:45:41|||||||01/06/2011 16:26:36|01/10/2011 21:45:12|01/10/2011 21:45:20||01/12/2011 19:25:25|02/01/2011 13:10:14|02/01/2011 20:04:07
Lo que tenía que hacer es localizar todas las fechas, luego cambiar de lugar los números, primero el año, luego el mes y al final el día. Es decir el primero grupo pasa al segundo lugar, el segundo grupo pasa al tercero y el tercero al primero. Todo lo demás debe quedar en su lugar. Suena complicado, pero con Python y Expresiones Regulares no lo es tanto.
Esta es la expresión regular utilizada:
'(d{2})/(d{2})/(d{4})s(.*)'
Cada parte de esta expresión regular se llama «patrón» y es posible formar grupos de patrones, simplemente encerrando el grupo entre paréntesis. Entonces lo que vemos en la expresión anterior es que se forman 4 grupos, cuando el texto coincide con la expresión.
El primer grupo está formado por dos dígitos «(d{2})» luego sigue una diagonal «/» luego otro grupo de dos dígitos «(d{2})», otra diagonal «/», un grupo de 4 dígitos «(d{2})» y el resto en la cadena de texto proporcionada, va en otro grupo. Esto coincide con la fecha proporcionada inicialmente:
01/03/2011 08:48:47
^ ^ ^ ^
| | | |
d{2}| | .*
| d{4}
d{2}
Si colocas la expresión regular en la página que mencioné anteriormente en el campo Regular Expression y la fecha en el campo Input 1, verás como funciona:
Vemos que marca como grupo 0, la cadena coincidente; como grupo 1, el 01 que para nosotros es el mes; el grupo 2, representa el día y contiene 03; el grupo 3, del año, tiene los dígitos 2011; por último, el grupo 4, contiene el resto de la cadena, que representa la hora.
Una vez resuelta la parte de búsqueda o match, debemos formar el reemplazo, lo que implica simplemente indicar el orden en que deseamos que aparezcan los grupos que hemos definido, en nuestro ejemplo, la cadena de reemplazo es la siguiente:
$3-$1-$2 $4
En Python, hay un módulo específico para el manejo de las expresiones regulares, y se llama re. Así nada mas, re. Pero es muy potente, por ejemplo, podemos darle a los grupos un nombre, para que la tarea de reemplazo, sea todavía más fácil, empezamos entonces nuestro guión de Python, para hacer el reemplazo.
#!/usr/bin/eval python
# -*- coding: utf-8 -*-
import re
buscar = re.compile('(?P<mes>d{2})/(?P<dia>d{2})/(?P<anio>d{4})s(.*)')
Así de simple es la cadena de búsqueda.
Ahora lo que sigue lo siguiente:
- es abrir el archivo,
- leer cada línea, y separarla por pipes («|»), buscar en cada parte la cadena de fecha y se la encontramos, realizar el reemplazo, escribir el resultado en un nuevo archivo.
En realidad, solo son dos pasos los que se necesitan, veamos porque:
a = open('datos.txt')
b = open('correcto.txt', 'w+')
t = ''
for linea in a.xreadlines():
for l in linea.split('|'):
b.write(t +'|' + buscar.sub(r'g<anio>/g<mes>/g<dia> 4', l))
t = ''
La variable «t» en el script, sirve para volver a unir la línea, cuando la separamos con la función split(), pero básicamente, este script lo que hace es abrir un archivo, leer cada línea, separarla por pipes, buscar las fechas y cambiar de lugar los números para que tengan el formato que deseamos.
En una máquina virtual como la mía VirtualBox, con 512Mb de memoria, OpenSuse 12.1, procesar las 179 mil líneas y cambiar las 4 millones de fechas, ocupa poquito más de un minuto. Seguro que no soy muy ambicioso, pero me parece bastante correcto el tiempo:
real 1m0.194s user 0m44.023s sys 0m2.200s
Seguiré contando mis aventuras en Python, y las compartiré con ustedes. Seguiremos estudiando las cadenas de texto en el próximo artículo.
El Zen de Python
Cómo usar las carpetas compartidas en VirtualBox
Cómo insertar imágenes en DocBook
Resuelto «Gtk-WARNING **: Unable to locate theme engine in module_path: “pixmap”,»



