Soluciones a los ejercicios de ficheros

Hace tres semanas os proponía dos ejercicios en el artículo de trabajo con ficheros en Python. Decían así:

  1. Crea un programa ejecutable por línea de comandos que reciba el nombre de un fichero (ruta relativa al directorio actual) como parámetro, y que devuelva el número de caracteres total, y el número total de palabras.
    1. Sólo puedes abrir el fichero una vez.
    2. Sólo puedes hacer una pasada al fichero (no vale volver a la posición inicial usando la función seek 
    3. No está permitido leer el fichero con read  ni con readlines 
  2. Crea otro programa ejecutable por línea de comandos que reciba el nombre de un fichero (ruta relativa al directorio actual) y que produzca otro, de igual nombre pero con extensión .pitando, con el contenido del primero fichero pero en mayúsculas.
    1. Sólo puedes abrir el fichero original una vez.
    2. Sólo puedes hacer una pasada al fichero (no vale volver a la posición inicial usando la función seek 
    3. No está permitido leer el fichero con read  ni con readlines 

Vamos a ver las soluciones paso a paso.

Contador de caracteres y palabras de ficheros

En este ejercicio debemos contar las palabras de un fichero y el total de los caracteres, de una sola pasada y sin leerlo a memoria completamente de una sola vez.

Lo primero, vamos a ver qué significa “una palabra”. Una palabra es un conjunto de letras del alfabeto que están separadas de otro conjunto semejante mediante espacios, signos de puntuación o separadores de línea. Por simplificarlo un poco, para resolver este ejercicio entenderemos “palabra” como un conjunto de letras del alfabeto separado de otro mediante cualquier otra cosa.

Para contar palabras debemos encontrar el primer caracter de la misma (la primera letra), y detectar el fin de la palabra. Saber si un caracter es una letra del alfabeto lo podemos hacer mediante comparación, como en la siguiente función:

def alfabetico(caracter):
    # Función que devuelve True si el caracter pasado como
    # parámetro es alfabético, False en otro caso

    alfabeto = ['a', 'á', 'b', 'c', 'd', 'e', 'é',
                'f', 'g', 'h', 'i', 'í', 'j', 'k',
                'l', 'm', 'n', 'ñ', 'o', 'ó', 'p',
                'q', 'r', 's', 't', 'u', 'ú', 'v',
                'w', 'x', 'y', 'z']
    
    if caracter.lower() in alfabeto:
        return True
    else:
        return False

Para saber si estamos en una palabra, te propongo el siguiente código:

# Variable de trabajo: total de palabras
palabras = 0

# Indicador de si estamos dentro de una
# palabra, o no
en_palabra = False

# Supón por el momento que la variable linea contiene
# una línea de texto
for caracter in linea:
    if alfabetico(caracter):
        if not en_palabra:
            en_palabra = True
            palabras = palabras + 1
        else:
            en_palabra = False

En él, lo que hago es para cada línea es lo siguiente:

  • Examino la línea caracter por caracter.
    • Si el caracter es alfabético,
      • Si no estoy dentro de una palabra ya, es que he encontrado una de ellas.
        • Sumo una palabra
        • Indicador de palabra a true 
      • Si estoy dentro de una palabra no hago nada, puesto que ya la he encontrado y por lo tanto ya la he contado.
    • Si no es alfabético, no estoy dentro de una palabra
      • Indicador de palabra a false 

Te recomiendo que pongas a prueba este algoritmo con un papel y lápiz, si no lo entiendes a la primera. También lo puedes probar en IDLE

Lo único que hay que hacer ahora es contar los caracteres además de las palabras, e integrar esta solución en la lectura de un fichero. Haré la lectura por líneas:

# Supón que nombreFichero ya tiene un nombre de fichero
# válido
fichero = open(nombreFichero)

# Variables de trabajo: totales 
palabras = 0
caracteres = 0

# Indicador de si estamos dentro de una
# palabra, o no
en_palabra = False

# Analizamos línea a línea
for linea in fichero:
    
    for caracter in linea:
        caracteres = caracteres +1

        # Para contar palabras debemos contar el número de
        # veces que nos encontramos un grupo de caracteres
        # alfabéticos
        if alfabetico(caracter):
            if not en_palabra:
                en_palabra = True
                palabras = palabras + 1
        else:
            en_palabra = False

    #Para la siguiente línea
    en_palabra = False

# Cerrar el fichero 
fichero.close()

Observa dónde he puesto el recuento de caracteres. Fíjate también que, antes de avanzar a la siguiente línea, reinicializo la variable que indica si estoy en una palabra: en_palabra = False 

Por último, debemos empaquetar todo este programa en forma de programa ejecutable. Para eso recuerda que necesitamos la librería sys  para poder acceder a la línea de comandos, y para poder salir del programa entregando un resultado al sistema operativo.

Este es el código fuente del programa final, para Mac OS X (para los otros sistemas operativos sólo cambia la primera línea):

#!/Library/Frameworks/Python.framework/Versions/3.4/bin/python3

import sys

def alfabetico(caracter):
    # Función que devuelve True si el caracter pasado como
    # parámetro es alfabético, False en otro caso

    alfabeto = ['a', 'á', 'b', 'c', 'd', 'e', 'é',
                'f', 'g', 'h', 'i', 'í', 'j', 'k',
                'l', 'm', 'n', 'ñ', 'o', 'ó', 'p',
                'q', 'r', 's', 't', 'u', 'ú', 'v',
                'w', 'x', 'y', 'z']
    
    if caracter.lower() in alfabeto:
        return True
    else:
        return False


# Analizar la línea de comandos y abrir el fichero
if len(sys.argv) != 2:
    print('Uso:', sys.argv[0], '')
    exit(1)

nombreFichero = sys.argv[1]
fichero = open(nombreFichero)


# Variables de trabajo: totales 
palabras = 0
caracteres = 0

# Indicador de si estamos dentro de una
# palabra, o no
en_palabra = False

# Analizamos línea a línea
for linea in fichero:
    
    for caracter in linea:
        caracteres = caracteres +1

        # Para contar palabras debemos contar el número de
        # veces que nos encontramos un grupo de caracteres
        # alfabéticos
        if alfabetico(caracter):
            if not en_palabra:
                en_palabra = True
                palabras = palabras + 1
        else:
            en_palabra = False

    #Para la siguiente línea
    en_palabra = False

# Cerrar el fichero 
fichero.close()

print ('Palabras:', palabras, '
Caracteres:', caracteres)

exit(0)

Pasar el contenido de ficheros a mayúsculas

Este ejercicio es mucho más fácil de lo que parece. Lo primero que tenemos que hacer, con respecto a lo que ya sabemos, es construir el nombre del fichero de salida encontrando el último punto. Para eso existe una función extremadamente útil que, aplicada a una cadena de texto, encuentra el primer índice de una subcadena empezando por el final: es la función rfind. Una vez encontrado el índice donde se encuentra el punto, usaremos una notación de Python llamada slicing (“loncheado”) que, aplicada sobre una cadena, sirve para trocearla. En su forma más básica, que de momento es la que nos interesa, funciona de la siguiente forma:

cadena[inicio:fin] # selecciona la subcadena formada por los caracteres que se
                   # encuentran entre la posición inicio (incluida) y la fin,
                   # no incluida

Por ejemplo, "hola caracola"[2:12] dará como resultado la cadena “la caracol”.

Poniendo todas estas ideas juntas, para obtener el nombre del fichero de salida tendremos el siguiente código:

# Construir el nombre de fichero de salida
# Primero, encontramos el último punto
punto = nombreFichero.rfind('.')

# Copiamos los caracteres hasta el punto, más la extensión '.pitando',
# a una nueva variable.
nombreFicheroSalida = nombreFichero[0:punto] + '.pitando'

El proceso de los ficheros es muy, muy fácil. Sólo tenemos que abrir los dos, y escribir en el segundo una versión pasada a mayúsculas de cada línea del primero. Para pasar a mayúsculas una cadena usaremos la función upper, que es complementaria a la ya conocida lower.

# Abrimos los dos ficheros
fichero = open(nombreFichero)
ficheroSalida = open(nombreFicheroSalida, 'x')

# Vamos línea a línea usando la función que ya conocemos
for linea in fichero:
    ficheroSalida.write(linea.upper())

# Cerramos los dos ficheros
fichero.close()
ficheroSalida.close()

Juntando todos estos pequeños exctractos es fácil componer la solución final como a continuación:

#!/Library/Frameworks/Python.framework/Versions/3.4/bin/python3

import sys

# Analizar la línea de comandos
if len(sys.argv) != 2:
    print('Uso:', sys.argv[0], '')
    exit(1)

nombreFichero = sys.argv[1]

# Construir el nombre de fichero de salida
# Primero, encontramos el último punto
punto = nombreFichero.rfind('.')

# Copiamos los caracteres hasta el punto, más la extensión '.pitando',
# a una nueva variable.
nombreFicheroSalida = nombreFichero[0:punto] + '.pitando'

# Abrimos los dos ficheros
fichero = open(nombreFichero)
ficheroSalida = open(nombreFicheroSalida, 'x')

# Vamos línea a línea usando la función que ya conocemos
for linea in fichero:
    ficheroSalida.write( linea.upper())

# Cerramos los dos ficheros
fichero.close()
ficheroSalida.close()

print("Resultado escrito en " + nombreFicheroSalida)
exit(0)

Y esto es todo, que no es poco.

Recuerda que, como siempre, puedes dejarme comentarios o dudas en los comentarios de este artículo, o a través del formulario de contacto o de la dirección de correo que allí se encuentra.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *