Siguiente Arriba Anterior Guía básica para pensar como un informático Índice

Capítulo 11

Comentarios

Archivos y excepciones

Mientras un programa se está ejecutando, sus datos se encuentran en la memoria. Cuando el programa termina o el equipo se apaga, los datos en la memoria desaparecen. Para grabar los datos de forma permanente, tienes que guardarlos en un archivo. Los archivos se suelen guardar en el disco duro, en un disquete o en un CD-ROM.

Cuando hay un gran número de archivos, éstos se suelen organizar en directorios (también llamados "carpetas"). Cada archivo es identificado por un nombre único o por una combinación de un nombre de archivo y un nombre de directorio.

Al leer y escribir archivos, los programas pueden intercambiar información entre ellos y generar formatos que se pueden imprimir como el PDF.

Trabajar con archivos se parece mucho a trabajar con libros. Para utilizar un libro, primero tienes que abrirlo. Después de abrirlo, tendrás que cerrarlo. Cuando el libro está abierto, puedes escribir en él o leerlo. En ambos casos, sabes dónde te encuentras en el libro. La mayoría de las veces se lee un libro página por página; pero también se puede leer saltando algunas páginas.

Todo esto también se puede aplicar a los archivos. Para abrir un archivo, especificas su nombre e indicas si lo quieres leer o escribir.

Al abrir un archivo creas un objeto de archivo. En este ejemplo, la variable f se refiere al nuevo objeto de archivo.

>>> f = open("test.dat","w")
>>> print f
<open file 'test.dat', mode 'w' at fe820>

La función abrir sigue dos parámetros. El primer parámetro es el nombre del archivo y el segundo es el modo. El modo "w" nos dice que estamos abriendo el archivo para escribir.

Si no hubiera ningún archivo llamado test.dat, éste se creará. Si ya hubiera uno, se reemplazará por el archivo que estamos escribiendo.

Si imprimimos el objeto de archivo, vemos el nombre del archivo, el modo y la localización del objeto.

Para introducir datos en el archivo, recurrimos al método write en el objeto de archivo:

>>> f.write("Ya es hora")
>>> f.write("de cerrar el archivo")

Al cerrar el archivo se le dice al sistema que ya hemos escrito y hace que el archivo esté disponible para su lectura:

>>> f.close()

Ahora podemos abrir el archivo otra vez -esta vez para leerlo- y leer los contenidos en forma de cadena. En este ejemplo, el parámetro del modo es "r" para la lectura:

>>> f = open("test.dat","r")

Si intentamos abrir un archivo que no existe, obtenemos un error:

>>> f = open("test.cat","r")
IOError: [Errno 2] No such file or directory: 'test.cat'

Como es lógico, el método read lee la información del archivo. Si no hay otros parámetros, lee el contenido completo del archivo:

>>> texto = f.read()
>>> print texto
Ya
es hora decerrar el archivo

No hay un espacio entre "de" y "cerrar" porque no hemos hecho un espacio entre las cadenas.

La sentencia read también puede seguir un parámetro que indique cuántos caracteres hay que leer:

>>> f = open("test.dat","r")
>>> print f.read(5)
Ya es

Si no hay suficientes caracteres en el archivo, read vuelve a colocar los caracteres que quedan. Cuando llegamos al final del archivo, read restituye la cadena vacía:

>>> print f.read(1000006)
hora de cerrar el archivo
>>> print f.read()

>>>

La siguiente función copia el archivo, leyendo y escribiendo hasta 50 caracteres a la vez. El primer parámetro es el nombre del archivo original, mientras que el segundo es el nombre del nuevo archivo:

def copiarArchivo(archivoAntiguo, archivoNuevo):
  f1 = open(archivoAntiguo, "r")
  f2 = open(archivoNuevo, "w")
  while 1:
    texto = f1.read(50)
    if texto == "":
      break
    f2.write(texto)
  f1.close()
  f2.close()
  return

La sentencia break es nueva. Al ejecutarla se rompe el bucle y el flujo de ejecución va hacia la primera sentencia después del bucle.

En este ejemplo, el bucle while es infinito porque el valor 1 es siempre verdadero. La única manera de salir del bucle es mediante la ejecución de break, lo que tiene lugar cuando texto es una cadena vacía; y suele suceder cuando llegamos al final del archivo.

11.1 Archivos de texto

Un archivo de texto es un archivo que contiene caracteres que se pueden imprimir y espacios en blanco, organizados en líneas, separados a su vez por caracteres de salto de línea. Ya que Python está diseñado específicamente para procesar archivos de texto, éste proporciona métodos para facilitar el trabajo.

Como demostración, se creará un archivo de texto con tres líneas de texto separadas por saltos de línea:

>>> f = open("test.dat","w")
>>> f.write(
"linea uno\nlinea dos\nlinea tres\n")
>>> f.close()
 

El método readline lee todos los caracteres hasta el próximo carácter de salto de línea, éste incluido:

>>> f = open("test.dat","r")
>>>
print f.readline()
linea uno

>>>
 

readlines devuelve todas las líneas restantes como una lista de cadenas:

>>> print f.readlines()
[
'linea dos\012', 'linea tres\012']
 

En este caso, el resultado está en el formato de lista, lo que significa que las cadenas aparecen entre comillas y el carácter de línea nueva aparece como una secuencia de escape <br>012.

Al final del archivo, readline devuelve la cadena vacía y readlines devuelve la lista vacía:

>>> print f.readline()

>>>
print f.readlines()
[]
 

Lo siguiente es un ejemplo de un procesador de líneas. filtrarArchivo hace una copia de archivoAntiguo, suprimiendo cualquier línea que comience con #:

def filtrarArchivo(archivoAntiguo, archivoNuevo):
  f1 = open(archivoAntiguo,
"r")
  f2 = open(archivoNuevo,
"w")
 
while 1:
    texto = f1.readline()
   
if text == "":
     
break
    if
texto[0] == '#':
     
continue
    f2.write(texto)
  f1.close()
  f2.close()
 
return
 

La sentencia continue termina el presente ciclo del bucle, pero continúa haciendo bucles. El flujo de movimientos de ejecución a la parte superior del bucle comprueba su condición y procede en consecuencia.

Por lo tanto, si texto es la cadena vacía, el bucle existe. Pero, si el primer carácter del texto es una almohadilla, el flujo de ejecución se dirige a la parte superior del bucle. Solamente en el caso de que ambas condiciones fallen, es cuando se copia el texto a un archivo nuevo. Comentarios

11.2 Cómo escribir variables

El parámetro write ha de ser una cadena. Por lo tanto, si se quieren añadir otras variables en un archivo, se deberán convertir, previamente, en cadenas. La manera más fácil de hacer esto, es con la función str:

>>> x = 52
>>> f.write (str(x))
 

Una alternativa sería usar el operador formato %. Cuando se utiliza con número enteros, % es el operador módulo. Pero, cuando el primer operando es una cadena, % es el operador formato.

El primer operando es la cadena formato, y el segundo operando es una tupla de expresiones. El resultado es una cadena que contiene los valores de la expresión, formateados según la cadena formato.

Un ejemplo sencillo, la secuencia formato "%d" significa que la primera expresión de la tupla debería formatearse como un número entero. En este caso, la letra d significa "decimal":

>>> coches = 52
>>>
"%d" % coches
'52'
 

El resultado es la cadena '52', que no debe confundirse con el valor del número entero 52.

La secuencia formato puede aparecer en cualquier sitio de la cadena formato, para que así se pueda añadir un valor en una frase:

>>> coches = 52
>>>
"En julio vendimos %d coches." % coches
'En julio vendimos 52 coches.'
 

La secuencia formato "%f" formatea el siguiente elemento del conjunto como un número decimal (float), y "%s" formatea el siguiente elemento como una cadena:

>>> "En %d dias hemos ganado %f millones de %s." % (34,6.1,'dólares')
'En 34 dias hemos ganado 6.100000 millones de dólares.'
 

El formato de decimales muestra, por defecto, seis decimales.

El número de expresiones en la tupla tiene que coincidir con el número de secuencias formato en la cadena. Además, los tipos de expresiones también deben coincidir con las secuencias formato:

>>> "%d %d %d" % (1,2)
TypeError:
not enough arguments for format string
>>>
"%d" % 'dolares'
TypeError: illegal argument type
for built-in operation
 

En el primer ejemplo, no hay suficientes expresiones; en el segundo ejemplo, el tipo de la expresión es errónea.

Para mayor control sobre el formato de los números, se puede especificar el número de dígitos como parte de la secuencia formato:

>>> "%6d" % 62
'    62'
>>>
"%12f" % 6.1
'    6.100000'
 

El número después del signo de porcentaje es el número mínimo de espacios que el número ocupará. Si el valor facilitado ocupa menos dígitos, se añadirán espacios a la izquierda. Si el número de espacios es negativo, se añadirán espacios a la derecha:

>>> "%-6d" % 62
'62    '
 

Para los números decimales, también se puede especificar el número de dígitos después del punto decimal:

>>> "%12.2f" % 6.1
'        6.10'
 

En este ejemplo, el resultado ocupa doce espacios e incluye dos dígitos tras el decimal. Este formato es útil para imprimir cantidades en dólares con puntos decimales alineados.

Por ejemplo, imagínate un diccionario que contiene los nombres de estudiantes como claves y la paga por horas como valores. Esta es la función que imprimirá los contenidos del diccionario como un informe formateado:

def informe (salarios) :
  estudiantes = salarios.keys()
  estudiantes.sort()
 
for estudiante in estudiantes :
   
print "%-20s %12.02f" % (estudiante, salarios[estudiante])
 

Para comprobar esta función, se creará un pequeño diccionario y se imprimirán los contenidos:

>>> salarios = {'maría': 6.23, 'jose': 5.45, 'joshua': 4.25}
>>> informe (salarios)
jose                          5.45
joshua                        4.25
maría                         6.23
 

Controlando la amplitud de cada valor, se garantiza que las columnas estarán alineadas, con tal de que los nombres se compongan de menos de veintiún caracteres y los salarios sean menores a mil millones de dólares por hora. Comentarios

11.3 Directorios

Cuando se crea un nuevo archivo al abrirlo y escribir en él, el nuevo archivo se sitúa en el directorio actual (donde estuvieras cuando ejecutaste el programa). De forma similar, cuando abres un archivo para leerlo, Python lo busca en el directorio habitual.

Si quieres abrir un archivo en cualquier otro lugar, tienes que especificar la ruta del archivo, que es el nombre del directorio (o carpeta) en el que el archivo está situado:

>>>   f = open("/usr/compartir/dicc/texto","r")
>>>   print f.readline()
Aarhus

En este ejemplo, se abre un archivo llamado texto que está situado en un directorio llamado dicc, que , a su vez, está situado en compartir, el cual está situado en usr, y que está situado en el directorio de mayor nivel del sistema llamado /.

No se puede utilizar / como parte de un nombre de archivo; ya que está reservado para funcionar como un delimitador entre el directorio y los nombres de los archivos.

El archivo /usr/compartir/dicc/texto contiene una lista de palabras en orden alfabético, de las cuáles la primera es el nombre de una universidad danesa. Comentarios

11.4 Cómo usar el módulo pickle

Si deseas insertar los valores en un archivo, debes convertirlos en cadenas. Ya se ha visto cómo se hace esto con str:

>>> f.write (str(12.3))
>>> f.write (str([1,2,3]))
 

El problema surge cuando se procesa el valor ya que lo que se obtiene es una cadena. El tipo original en que se presentaba la información se ha perdido. De hecho, no se puede distinguir dónde termina un valor y comienza el siguiente:

>>>   f.readline()
'12.3[1, 2, 3]'
 

La solución la encontramos al usar el módulo pickle, llamado así porque "mantiene" las estructuras de datos. El módulo pickle contiene las órdenes necesarias. Si deseas usarlo, importa el módulo pickle y luego abre el archivo como siempre:

>>> import pickle
>>> f = open("test.pck","w")
 

Si deseas almacenar una estructura de datos, usa el método dump (vaciado) y luego cierra el archivo como siempre:

>>> pickle.dump(12.3, f)
>>> pickle.dump([1,2,3], f)
>>> f.close()
 

Después, podemos abrir el archivo para leerlo y cargar las estructuras de datos que hemos vaciado:

>>> f = open("test.pck","r")
>>> x = pickle.load(f)
>>> x
12.3
>>> type(x)
<type 'float'>
>>> y = pickle.load(f)
>>> y
[1, 2, 3]
>>> type(y)
<type 'list'>
 

Cada vez que se ordena load, conseguiremos un valor único del archivo, en su tipo original. Comentarios

11.5 Excepciones

Siempre que se produzca un error de tiempo de ejecución, se creará una excepción. Normalmente, el programa se detiene y Python imprime un mensaje de error.

Por ejemplo, dividir por cero crea una excepción:

>>> print 55/0
ZeroDivisionError: integer division or modulo
 

Y también acceder a un elemento en una lista inexistente:

>>> a = []
>>> print a[5]
IndexError: list index out of range
 

O acceder a una clave que no se encuentra en el diccionario:

>>> b = {}
>>> print b['que']
KeyError: que
 

En cada caso, el mensaje de error tiene dos partes: el tipo de error antes de los dos puntos y las especificidades sobre el error después de los dos puntos. Normalmente, Python también imprime una traza de errores (o traceback) desde donde estaba el programa, pero nosotros lo hemos omitido de los ejemplos.

A veces, queremos ejecutar una operación que podría provocar una excepción, pero no queremos que el programa se detenga. Podemos gestionar la excepción usando las sentencias try y except.

Por ejemplo, podemos indicar al usuario que escriba el nombre de un archivo e intentar abrirlo. En caso de que el archivo no exista, no queremos que el programa se cuelgue; por tanto sólo gestionamos la excepción.

nombre = raw_input('Escribe un nombre de archivo: ')
try:
  f = open (nombre, "r")
except:
  print 'No existe ningún archivo llamado', nombre
 

La sentencia try ejecuta las sentencias del primer bloque. Si no se producen las excepciones, ignora la sentencia except. Si se produce alguna excepción, se ejecutan las sentencias en la rama except y luego continúa.

Podemos encapsular esta habilidad en una función: existe, que toma un nombre de archivo y si existe nos indica que es verdadero (1) y falso (0) en caso de no existir.

def existe(nombre):
  try:
    f = open(nombre)
    f.close()
    return 1
  except:
    return 0
 

Puedes usar varios bloques de except para gestionar los diferentes tipos de excepciones. El Manual de referencia de Python contiene los detalles.

Si el programa detecta una condición de error, puedes hacerle saltar una excepción. Aquí hay un ejemplo que necesita información del usuario y comprueba el valor 17. Asumiendo que, por cualquier razón, 17 no es una entrada válida, haremos saltar una excepción.

def númeroEntrada () :
  x = input ('Elige un número: ')
  if x == 17 :
    raise 'ErrorNúmeroIncorrecto', '17 es un número incorrecto'
  return x
 

La sentencia raise tiene dos parámetros: el tipo de excepción y la información específica sobre el error. El ErrorNúmeroIncorrecto es un nuevo tipo de excepción que hemos inventado para esta aplicación.

Si la función llamada númeroEntrada gestiona el error, el programa puede continuar; de lo contrario, Python imprime el mensaje de error y continúa:

>>> númeroEntrada ()
Elige un número: 17
ErrorNúmeroIncorrecto: 17 es un número incorrecto
 

El mensaje de error incluye el tipo de excepción y la información adicional que se proporcione.

Como ejercicio, escribe una función que use númeroEntrada para introducir un número en el teclado y que gestione la excepción del ErrorNúmeroIncorrecto.

Comentarios

11.6 Glosario

archivo
Una entidad con un nombre, normalmente guardada en un disco duro, disquete o CD-ROM, que contiene una serie de caracteres.
directorio
Una colección de archivos con un nombre, también llamado carpeta.
ruta
Una secuencia de nombres de directorios que especifican la situación exacta de un archivo.
archivo de texto
Un archivo que contiene caracteres que se pueden imprimir organizados en líneas separadas por caracteres de salto de línea.
sentencia de ruptura
Una sentencia que implica que el flujo de ejecución salga de un bucle.
sentencia continue
Sentencia que provoca que finalice el actual recorrido del bucle. El flujo de ejecución se dirige a la parte superior del bucle, evalúa la condición y procede en consecuencia.
operador formato
El operador % toma una cadena formato y una tupla de expresiones y, produce una cadena que incluye las expresiones, formateadas según la cadena formato.
cadena formato
Una cadena que contiene caracteres que se pueden imprimir y secuencias formato que indican cómo formatear valores.
secuencia formato
Una secuencia de caracteres que comienza con % e indica cómo formatear un valor.
módulo pickle
Módulo que permite escribir un valor de datos en un archivo junto con su tipo de información de manera que se pueda reconstituir más tarde.
excepción
Un error que se produce en el momento de ejecución.
gestionar
Evitar que una excepción termine el programa utilizando las sentencias try y except.
sentencia raise (hacer saltar)
La sentencia raise se utiliza para señalar una excepción.


Siguiente Arriba Anterior Guía básica para pensar como un informático Índice