Algo de python



Python: Expresiones regulares

Las expresiones regulares, también llamadas regex o regexp, consisten en patrones que describen conjuntos de cadenas de caracteres.


Algo parecido sería escribir en la línea de comandos de Windows


dir *.exe


‘*.exe’ sería una “expresión regular” que describiría todas las cadenas de caracteres que empiezan con cualquier cosa seguida de ‘.exe’, es decir, todos los archivos exe.


El trabajo con expresiones regulares en Python se realiza mediante el módulo re, que data de Python 1.5 y que proporciona una sintaxis para la creación de patrones similar a la de Perl. En Python 1.6 el módulo se reescribió para dotarlo de soporte de cadenas unicode y mejorar su rendimiento.


El módulo re contiene funciones para buscar patrones dentro de una cadena (search), comprobar si una cadena se ajusta a un determinado criterio descrito mediante un patrón (match), dividir la cadena usando las ocurrencias del patrón como puntos de ruptura (split) o para sustituir todas las ocurrencias del patrón por otra cadena (sub). Veremos estas funciones y alguna más en la próxima sección, pero por ahora, aprendamos algo más sobre la sintaxis de las expresiones regulares.
EXPRESIONES REGULARES: PATRONES


La expresión regular más sencilla consiste en una cadena simple, que describe un conjunto compuesto tan solo por esa misma cadena. Por ejemplo, veamos cómo la cadena “python” coincide con la expresión regular “python” usando la función match:import re if re.match("python", "python"): print "cierto"



Si quisiéramos comprobar si la cadena es python, jython, cython o cualquier otra cosa que termine en “ython”, podríamos utilizar el carácter comodín, el punto ‘.’:re.match(".ython", "python") re.match(".ython", "jython")



La expresión regular “.ython” describiría a todas las cadenas que consistan en un carácter cualquiera, menos el de nueva línea, seguido de “ython”. Un carácter cualquiera y solo uno. No cero, ni dos, ni tres.


En el caso de que necesitáramos el carácter ‘.’ en la expresión regular, o cualquier otro de los caracteres especiales que veremos a continuación, tendríamos que escaparlo utilizando la barra invertida.


Para comprobar si la cadena consiste en 3 caracteres seguidos de un punto, por ejemplo, podríamos utilizar lo siguiente:re.match("...\.", "abc.")



Si necesitáramos una expresión que sólo resultara cierta para las cadenas “python”, “jython” y “cython” y ninguna otra, podríamos utilizar el carácter ‘|’ para expresar alternativa escribiendo los tres subpatrones completos:re.match("python|jython|cython", "python")



o bien tan solo la parte que pueda cambiar, encerrada entre paréntesis, formando lo que se conoce como un grupo. Los grupos tienen una gran importancia a la hora de trabajar con expresiones regulares y este no es su único uso, como veremos en la siguiente sección.re.match("(p|j|c)ython", "python")



Otra opción consistiría en encerrar los caracteres ‘p’, ‘j’ y ‘c’ entre corchetes para formar una clase de caracteres, indicando que en esa posición puede colocarse cualquiera de los caracteres de la clase.re.match("[pjc]ython", "python")



¿Y si quisiéramos comprobar si la cadena es python0, python1, python2, … , python9? En lugar de tener que encerrar los 10 dígitos dentro de los corchetes podemos utilizar el guión, que sirve para indicar rangos. Por ejemplo a-d indicaría todas las letras minúsculas de la ‘a’ a la ‘d’; 0-9 serían todos los números de 0 a 9 inclusive.re.match("python[0-9]", "python0")



Si quisiéramos, por ejemplo, que el último carácter fuera o un dígito o una letra simplemente se escribirían dentro de los corchetes todos los criterios, uno detras de otro.re.match("python[0-9a-zA-Z]", "pythonp")



Es necesario advertir que dentro de las clases de caracteres los caracteres especiales no necesitan ser escapados. Para comprobar si la cadena es “python.” o “python,”, entonces, escribiríamos:re.match("python[.,]", "python.")



y nore.match("python[\.,]", "python.")



ya que en este último caso estaríamos comprobando si la cadena es “python.”, “python,” o “python\”.


Los conjuntos de caracteres también se pueden negar utilizando el símbolo ‘^’. La expresión “python[^0-9a-z]“, por ejemplo, indicaría que nos interesan las cadenas que comiencen por “python” y tengan como último carácter algo que no sea ni una letra minúscula ni un número.re.match("python[^0-9a-z]", "python+")



El uso de [0-9] para referirse a un dígito no es muy común, ya que, al ser la comprobación de que un carácter es un dígito algo muy utilizado, existe una secuencia especial equivalente: ‘\d’. Existen otras secuencias disponibles que listamos a continuación:


\d


un dígito. Equivale a [0-9]


\D


cualquier carácter que no sea un dígito. Equivale a [^0-9]


\w


Cualquier caracter alfanumérico. Equivale a [a-zA-Z0-9_].


\W


Cualquier carácter no alfanumérico. Equivale a [^a-zA-Z0-9_].


\s


Cualquier carácter en blanco. Equivale a [ \t\n\r\f\v]


\S


Cualquier carácter que no sea un espacio en blanco. Equivale a [^ \t\n\r\f\v]


Veamos ahora cómo representar repeticiones de caracteres, dado que no sería de mucha utilidad tener que, por ejemplo, escribir una expresión regular con 30 caracteres ‘\d’ para buscar números de 30 dígitos. Para este menester tenemos los caracteres especiales ‘+’, ‘*’, ‘?”, además de las llaves ‘{}’.


El carácter ‘+’ indica que lo que tenemos a la izquierda, sea un carácter como ‘a’, una clase como ‘[abc]‘ o un subpatrón como (abc), puede encontrarse una o mas veces. Por ejemplo la expresión regular “python+” describiría las cadenas “python”, “pythonn”, “pythonnn”, pero no “pytho”, ya que debe haber al menos una n.


El carácter ‘*’ es similar a ‘+’, pero en este caso lo que se sitúa a su izquierda puede encontrarse cero o mas veces.


El carácter ‘?’ indica opcionalidad, es decir, lo que tenemos a la izquierda puede o no aparecer (puede aparecer 0 o 1 veces).


Finalmente las llaves sirven para indicar el número de veces exacto que puede aparecer el carácter de la izquierda, o bien un rango de veces que puede aparecer. Por ejemplo {3} indicaría que tiene que aparecer exactamente 3 veces, {3,8} indicaría que tiene que aparecer de 3 a 8 veces, {,8} de 0 a 8 veces y {3,} tres veces o mas (las que sean).


Otro elemento interesante en las expresiones regulares, para terminar, es la especificación de las posiciones en que se tiene que encontrar la cadena, esa es la utilidad de ^ y $, que indican que el elemento sobre el que actúa debe ir al principio de la cadena o al final de esta.


La cadena “http://mundogeek.net”, por ejemplo, se ajustaría a la expresión regular “^http”, mientras que la cadena “El protocolo es http” no lo haría, ya que el http no se encuentra al principio de la cadena.
EXPRESIONES REGULARES: USANDO EL MÓDULO RE


Ya hemos visto por encima cómo se utiliza la función match del módulo re para comprobar si una cadena se ajusta a un determinado patrón. El primer parámetro de la función es la expresión regular, el segundo, la cadena a comprobar y existe un tercer parámetro opcional que contiene distintos flags que se pueden utilizar para modificar el comportamiento de las expresiones regulares.


Algunos ejemplos de flags del módulo re son re.IGNORECASE, que hace que no se tenga en cuenta si las letras son mayúsculas o minúsculas o re.VERBOSE, que hace que se ignoren los espacios y los comentarios en la cadena que representa la expresión regular.


El valor de retorno de la función será None en caso de que la cadena no se ajuste al patrón o un objeto de tipo MatchObject en caso contrario. Este objeto MatchObject cuenta con métodosstart y end que devuelven la posición en la que comienza y finaliza la subcadena reconocida y métodos group y groups que permiten acceder a los grupos que propiciaron el reconocimiento de la cadena.


Al llamar al método group sin parámetros se nos devuelve el grupo 0 de la cadena reconocida. El grupo 0 es la subcadena reconocida por la expresión regular al completo, aunque no existan paréntesis que delimiten el grupo.


>>> mo = re.match(“http://.+\net”, “http://mundogeek.net”)
>>> print mo.group()


http://mundogeek.net


Podríamos crear grupos utilizando los paréntesis, como aprendimos en la sección anterior, obteniendo así la parte de la cadena que nos interese.


>>> mo = re.match(“http://(.+)\net”, “http://mundogeek.net”)
>>> print mo.group(0)


http://mundogeek.net


>>> print mo.group(1)
mundogeek


El método groups, por su parte, devuelve una lista con todos los grupos, exceptuando el grupo 0, que se omite.


>>> mo = re.match(“http://(.+)\(.{3})”, “http://mundogeek.net”)
>>> print mo.groups()
(‘mundogeek’, ‘net’)


La función search del módulo re funciona de forma similar a match; contamos con los mismos parámetros y el mismo valor de retorno. La única diferencia es que al utilizar match la cadena debe ajustarse al patrón desde el primer carácter de la cadena, mientras que con searchbuscamos cualquier parte de la cadena que se ajuste al patrón. Por esta razón el método start de la función match siempre devolverá 0, mientras que en el caso de search esto no tiene por qué ser así.


Otra función de búsqueda del módulo re es findall. Este toma los mismos parámetros que las dos funciones anteriores, pero devuelve una lista con las subcadenas que cumplieron el patrón.


Otra posibilidad, si no queremos todas las coincidencias, es utilizar finditer, que devuelve un iterador con el que consultar uno a uno los distintos MatchObject.


Las expresiones regulares no solo permiten realizar búsquedas o comprobaciones, sino que, como comentamos anteriormente, también tenemos funciones disponibles para dividir la cadena o realizar reemplazos.


La función split sin ir más lejos toma como parámetros un patrón, una cadena y un entero opcional indicando el número máximo de elementos en los que queremos dividir la cadena, y utiliza el patrón a modo de puntos de separación para la cadena, devolviendo una lista con las subcadenas.


La función sub toma como parámetros un patrón a sustituir, una cadena que usar como reemplazo cada vez que encontremos el patrón, la cadena sobre la que realizar las sustituciones, y un entero opcional indicando el número máximo de sustituciones que queremos realizar.


Al llamar a estos métodos lo que ocurre en realidad es que se crea un nuevo objeto de tipoRegexObject que representa la expresión regular, y se llama a métodos de este objeto que tienen los mismos nombres que las funciones del módulo.


Si vamos a utilizar un mismo patrón varias veces nos puede interesar crear un objeto de este tipo y llamar a sus métodos nosotros mismos; de esta forma evitamos que el intérprete tenga que crear un nuevo objeto cada vez que usemos el patrón y mejoraremos el rendimiento de la aplicación.


Para crear un objeto RegexObject se utiliza la función compile del módulo, al que se le pasa como parámetro la cadena que representa el patrón que queremos utilizar para nuestra expresión regular y, opcionalmente, una serie de flags de entre los que comentamos anteriormente.

Script python para renombrar archivos


import os
for fn in os.listdir("."):
  if fn[-4:] == ".jpg":
   nombre=fn[0:-4:]
   nn=''
   nn=nombre+'-thumb.jpg'
   os.rename(fn,nn)

Formateo de String en Python


La sentencia print, o más bien las cadenas que imprime, permiten también utilizar técnicas avanzadas de formateo, de forma similar al sprintf de C.
Veamos un ejemplo bastante simple:

print "Hola %s" % "mundo"
print "%s %s" % ("Hola", "mundo")
 
Lo que hace la primera línea es introducir los valores a la derecha del símbolo ‘%’ (la cadena “mundo”) en las posiciones indicadas por los especificadores de conversión de la cadena a la izquierda del símbolo ‘%’, tras convertirlos al tipo adecuado.

En el segundo ejemplo, vemos cómo se puede pasar más de un valor a sustituir, por medio de una tupla.

En este ejemplo sólo tenemos un especificador de conversión: ‘%s’.
Los especificadores más sencillos están formados por el símbolo ‘%’ seguido de una letra que indica el tipo con el que formatear el valor.

 Por ejemplo:

Especificador Formato
%s Cadena
%d Entero
%o Octal
%x Hexadecimal
%f Real

Se puede introducir un número entre el ‘%’ y el carácter que indica el tipo al que formatear, indicando el número mínimo de caracteres que queremos que ocupe la cadena. Si el tamaño de la cadena resultante es menor que este número, se añadirán espacios a la izquierda de la cadena. En el caso de que el número sea negativo, ocurrirá exactamente lo mismo, sólo que los espacios se añadirán a la derecha de la cadena.

>>> print “%10s mundo” % “Hola”
______Hola mundo

>>> print “%-10s mundo” % “Hola”
Hola_______mundo

En el caso de los reales es posible indicar la precisión a utilizar precediendo la ‘f’ de un punto seguido del número de decimales que queremos mostrar:

>>> from math import pi
>>> print “%.4f” % pi
3.1416

La misma sintaxis se puede utilizar para indicar el número de caracteres de la cadena que queremos mostrar
>>> print “%.4s” % “hola mundo”
hola

Archivos

Los ficheros en Python son objetos de tipo file creados mediante la función open (abrir). Esta función toma como parámetros una cadena con la ruta al fichero a abrir, que puede ser relativa o absoluta; una cadena opcional indicando el modo de acceso (si no se especifica se accede en modo lectura) y, por último, un entero opcional para especificar un tamaño de buffer distinto del utilizado por defecto.
El modo de acceso puede ser cualquier combinación lógica de los siguientes modos:
  • ‘r’: read, lectura. Abre el archivo en modo lectura. El archivo tiene que existir previamente, en caso contrario se lanzará una excepción de tipo IOError.
  • ‘w’: write, escritura. Abre el archivo en modo escritura. Si el archivo no existe se crea. Si existe, sobreescribe el contenido.
  • ‘a’: append, añadir. Abre el archivo en modo escritura. Se diferencia del modo ‘w’ en que en este caso no se sobreescribe el contenido del archivo, sino que se comienza a escribir al final del archivo.
  • ‘b’: binary, binario.
  • ‘+’: permite lectura y escritura simultáneas.
  • ‘U’: universal newline, saltos de línea universales. Permite trabajar con archivos que tengan un formato para los saltos de línea que no coincide con el de la plataforma actual (en Windows se utiliza el caracter CR LF, en Unix LF y en Mac OS CR).
f = open("archivo.txt", "w")
Tras crear el objeto que representa nuestro archivo mediante la función open podremos realizar las operaciones de lectura/escritura pertinentes utilizando los métodos del objeto que veremos en las siguientes secciones.
Una vez terminemos de trabajar con el archivo debemos cerrarlo utilizando el método close.

Lectura de archivos

Para la lectura de archivos se utilizan los métodos read, readline y realines.
El método read devuelve una cadena con el contenido del archivo o bien el contenido de los primeros n bytes, si se especifica el tamaño máximo a leer.
completo = f.read()

parte = f2.read(512)
El método readline sirve para leer las líneas del fichero una por una. Es decir, cada vez que se llama a este método, se devuelve el contenido del archivo desde el puntero hasta que se encuentra un carácter de nueva línea, incluyendo este carácter.
while True:
      linea = f.readline()
      if not linea: break
      print line
Por último, readlines, funciona leyendo todas las líneas del archivo y devolviendo una lista con las líneas leídas.

Escritura de archivos

Para la escritura de archivos se utilizan los método write y writelines. Mientras el primero funciona escribiendo en el archivo una cadena de texto que toma como parámetro, el segundo toma como parámetro una lista de cadenas de texto indicando las líneas que queremos escribir en el fichero.

Mover el puntero de lectura/escritura en ficheros

Hay situaciones en las que nos puede interesar mover el puntero de lectura/escritura a una posición determinada del archivo. Por ejemplo si queremos empezar a escribir en una posición determinada y no al final o al principio del archivo.
Para esto se utiliza el método seek que toma como parámetro un número positivo o negativo a utilizar como desplazamiento. También es posible utilizar un segundo parámetro para indicar desde dónde queremos que se haga el desplazamiento: 0 indicará que el desplazamiento se refiere al principio del fichero (comportamiento por defecto), 1 se refiere a la posición actual, y 2, al final del fichero.
Para determinar la posición en la que se encuentra actualmente el puntero se utiliza el método tell, que devuelve un entero indicando la distancia en bytes desde el principio del fichero.


Para crear un objeto de la clase file debemos utilizar la función open. Cuando llamamos a dicha función le pasamos como primer parámetro el nombre del archivo de texto y el modo de apertura del mismo:
open(nombre del archivo,modo)
Si el archivo de texto se encuentra en la misma carpeta que nuestro programa no necesitamos indicar el path (camino). Los modos de apertura del archivo de texto pueden ser:
  • 'r' Abre el archivo para lectura (debe existir el archivo)
  • 'w' Crea el archivo y lo abre para escribir
  • 'a' Abre el archivo para escribir. Se crea si el archivo no existe. Solo podemos agregar datos al final

Creación de un archivo de texto

El siguiente algoritmo crea en disco un archivo de texto llamado 'datos.txt' y no graba datos. Si queremos luego de ejecutar el programa podemos verificar la existencia del archivo en la misma carpeta donde almacenamos nuestro programa.
Cuando lo grabamos el archivo fuente en Python debemos darle un nombre al programa (por ejemplo texto1.py)
def creaciontxt():
    archi=open('datos.txt','w')
    archi.close()

creaciontxt()
Creamos una función llamada creaciontxt donde primero llamamos a la función open pasando como parámetros el nombre del archivo de texto a crear y el modo de apertura ('w')
La función open retorna la referencia del objeto file. Luego llamamos al método close de la clase file. Si luego queremos ver si se a creado el archivo de textos podemos hacerlo desde algun explorador de archivos, en la carpeta donde se encuentra nuestro programa en Python veremos un archivo llamado 'datos.txt' que tiene un tamaño de 0 bytes.

Grabación de líneas en el archivo de texto

def creartxt():
    archi=open('datos.txt','w')
    archi.close()

def grabartxt():
    archi=open('datos.txt','a')
    archi.write('Linea 1\n')
    archi.write('Linea 2\n')
    archi.write('Linea 3\n')
    archi.close()

creartxt()
grabartxt()
La función creartxt es similar al ejemplo anterior, y la función grabartxt tiene por objetivo abrir el archivo en modo de agregado. Cada vez que grabamos un string en el archivo de texto insertamos un salto de línea '\n'. Finalmente liberamos el archivo llamando al método close.
Para ver el contenido de nuestro archivo de texto debemos utilizar un editor de texto (bloc de notas o el mismo entorno del Idle nos puede servir para ver el contenido de un archivo txt)

Lectura línea a línea de un archivo de texto

La clase file tiene un método llamado readline() que retorna toda una línea del archivo de texto y deja posicionado el puntero de archivo en la siguiente línea. Cuando llega al final del archivo readline retorna un string vacío.
def creartxt():
    archi=open('datos.txt','w')
    archi.close()

def grabartxt():
    archi=open('datos.txt','a')
    archi.write('Linea 1\n')
    archi.write('Linea 2\n')
    archi.write('Linea 3\n')
    archi.close()

def leertxt():
    archi=open('datos.txt','r')
    linea=archi.readline()
    while linea!="":
        print linea
        linea=archi.readline()
     archi.close()


creartxt()
grabartxt()
leertxt()
Luego de de abrir el archivo para lectura procedemos a leer la primer línea:
    archi=open('datos.txt','r')
    linea=archi.readline()
El while se repite mientras el método readline() no retorne un string vacío. Dentro del while procedemos a imprimir la línea que acabamos de leer y leemos la siguiente (el método readline() retorna el contenido de toda la línea inclusive el salto de línea \n):
    while linea!="":
        print linea
        linea=archi.readline()
Podemos leer todo el contenido de un archivo de texto y almacenarlo en una lista (esto tiene sentido si el archivo de texto no es muy grande):
def creartxt():
    archi=open('datos.txt','w')
    archi.close()

def grabartxt():
    archi=open('datos.txt','a')
    archi.write('Linea 1\n')
    archi.write('Linea 2\n')
    archi.write('Linea 3\n')
    archi.close()

def leertxtenlista():
    archi=open('datos.txt','r')
    lineas=archi.readlines()
    print lineas
    archi.close()

creartxt()
grabartxt()
leertxtenlista()
Utilizamos el método readlines() en lugar de readline(). El método readlines() retorna una lista con cada línea del archivo de texto.
Recordemos que podemos procesar luego cada elemento de la lista recorriéndola con una estructura repetitiva:
def leertxtenlista():
    archi=open('datos.txt','r')
    lineas=archi.readlines()
    for li in lineas:
        print li
    archi.close()

No hay comentarios:

Publicar un comentario