Leer un archivo l铆nea por l铆nea en Python

    Introducci贸n

    A lo largo de mi vida laboral he tenido la oportunidad de utilizar muchos conceptos y tecnolog铆as de programaci贸n para hacer innumerables cosas. Algunas de estas cosas involucran frutos de mi trabajo de valor relativamente bajo, como la automatizaci贸n de lo propenso a errores o mundano como la generaci贸n de informes, la automatizaci贸n de tareas y el reformateo de datos generales. Otros han sido mucho m谩s valiosos, como el desarrollo de productos de datos, aplicaciones web y canalizaciones de an谩lisis y procesamiento de datos. Una cosa que es notable acerca de casi todos estos proyectos es la necesidad de simplemente abrir un archivo, analizar su contenido y hacer algo con 茅l.

    Sin embargo, 驴qu茅 hace cuando el archivo que est谩 intentando consumir es bastante grande? 驴Qu茅 pasa si el archivo tiene varios GB de datos o m谩s? Una vez m谩s, este ha sido otro aspecto frecuente de mi carrera de programaci贸n, que se ha pasado principalmente en el sector de BioTech, donde es com煤n encontrar archivos de m谩s de 1 TB de tama帽o.

    La respuesta a este problema es leer fragmentos de un archivo a la vez, procesarlo y luego liberarlo de la memoria para poder extraer y procesar otro fragmento hasta que se haya procesado todo el archivo masivo. Si bien depende del programador determinar un tama帽o de fragmento adecuado, para muchas aplicaciones es adecuado procesar un archivo una l铆nea a la vez.

    A lo largo de este art铆culo, cubriremos una serie de ejemplos de c贸digo para mostrar c贸mo leer archivos l铆nea por l铆nea. En caso de que desee probar algunos de estos ejemplos usted mismo, el c贸digo utilizado en este art铆culo se puede encontrar en el siguiente Repositorio de GitHub.

    E / S de archivo b谩sico en Python

    Al ser un gran lenguaje de programaci贸n de prop贸sito general, Python tiene una serie de funciones de E / S de archivos muy 煤tiles en su biblioteca est谩ndar de funciones y m贸dulos integrados. El incorporado open() funci贸n es lo que se utiliza para abrir una objeto de archivo ya sea para leer o escribir.

    fp = open('path/to/file.txt', 'r')
    

    los open() La funci贸n toma m煤ltiples argumentos. Nos centraremos en los dos primeros, siendo el primero un par谩metro de cadena posicional que representa la ruta al archivo que se debe abrir. El segundo par谩metro opcional tambi茅n es una cadena, que especifica el modo de interacci贸n que desea para el objeto de archivo que devuelve la llamada a la funci贸n. Los modos m谩s comunes se enumeran en la tabla siguiente, siendo el valor predeterminado ‘r’ para lectura.

    Descripci贸n del modo

    rAbierto para leer texto sin formato
    wAbierto para escribir texto sin formato
    aAbra un archivo existente para agregar texto sin formato
    rbAbierto para leer datos binarios
    wbAbierto para escribir datos binarios

    Una vez que haya escrito o le铆do todos los datos deseados para un objeto de archivo, debe cerrar el archivo para que los recursos puedan reasignarse en el sistema operativo en el que se ejecuta el c贸digo.

    fp.close()
    

    A menudo ver谩 muchos fragmentos de c贸digo en Internet o en programas en la naturaleza que no cierran expl铆citamente los objetos de archivo que se han generado de acuerdo con el ejemplo anterior. Siempre es una buena pr谩ctica cerrar un recurso de objeto de archivo, pero muchos de nosotros somos demasiado perezosos u olvidadizos para hacerlo o pensamos que somos inteligentes porque la documentaci贸n sugiere que un objeto de archivo abierto se cerrar谩 autom谩ticamente una vez que finalice un proceso. Este no es siempre el caso.

    En lugar de insistir en lo importante que es llamar siempre close() en un objeto de archivo, me gustar铆a proporcionar una forma alternativa y m谩s elegante de abrir un objeto de archivo y asegurarme de que el int茅rprete de Python se limpie despu茅s de nosotros 馃檪

    with open('path/to/file.txt') as fp:
        # do stuff with fp
    

    Simplemente usando el with palabra clave (introducida en Python 2.5) para envolver nuestro c贸digo para abrir un objeto de archivo, las partes internas de Python har谩n algo similar al siguiente c贸digo para asegurarse de que no importa qu茅 objeto de archivo se cierre despu茅s de su uso.

    try:
        fp = open('path/to/file.txt')
    
        # do stuff with fp
    finally:
        fp.close()
    

    Cualquiera de estos dos m茅todos es adecuado, siendo el primer ejemplo la forma m谩s “Pythonic”.

    Leer l铆nea por l铆nea

    Ahora, vayamos a leer un archivo. El objeto de archivo devuelto por open() tiene tres m茅todos expl铆citos comunes (read, readliney readlines) para leer datos y una forma m谩s impl铆cita.

    los read El m茅todo leer谩 todos los datos en una cadena de texto. Esto es 煤til para archivos m谩s peque帽os en los que le gustar铆a realizar una manipulaci贸n de texto en todo el archivo o en cualquier otra cosa que le convenga. Entonces hay readline que es una forma 煤til de leer solo cantidades incrementales de l铆nea individual a la vez y devolverlas como cadenas. El 煤ltimo m茅todo expl铆cito, readlines, leer谩 todas las l铆neas de un archivo y las devolver谩 como una lista de cadenas.

    Como se mencion贸 anteriormente, puede usar estos m茅todos para cargar solo peque帽as partes del archivo a la vez. Para hacer esto con estos m茅todos, puede pasarles un par谩metro que indique cu谩ntos bytes cargar a la vez. Este es el 煤nico argumento que aceptan estos m茅todos.

    A continuaci贸n se muestra una implementaci贸n para leer un archivo de texto una l铆nea a la vez, que se realiza a trav茅s del readline() m茅todo.

    Nota: Durante el resto de este art铆culo, demostrar茅 c贸mo leer en el texto del libro La “Il铆ada de Homero”, que se puede encontrar en gutenberg.org, as铆 como en el repositorio de GitHub donde est谩 el c贸digo para este art铆culo.

    En readline.py encontrar谩 el siguiente c贸digo. En la terminal si corres $ python readline.py puedes ver el resultado de leer todas las l铆neas de la Il铆ada, as铆 como sus n煤meros de l铆nea.

    filepath="Iliad.txt"
    with open(filepath) as fp:
       line = fp.readline()
       cnt = 1
       while line:
           print("Line {}: {}".format(cnt, line.strip()))
           line = fp.readline()
           cnt += 1
    

    El fragmento de c贸digo anterior abre un objeto de archivo almacenado como una variable llamada fp, luego lee en una l铆nea a la vez llamando readline en ese objeto de archivo iterativamente en un while loop y lo imprime en la consola.

    Al ejecutar este c贸digo, deber铆a ver algo como lo siguiente:

    $ python forlinein.py 
    Line 0: BOOK I
    Line 1: 
    Line 2: The quarrel between Agamemnon and Achilles--Achilles withdraws
    Line 3: from the war, and sends his mother Thetis to ask Jove to help
    Line 4: the Trojans--Scene between Jove and Juno on Olympus.
    Line 5: 
    Line 6: Sing, O goddess, the anger of Achilles son of Peleus, that brought
    Line 7: countless ills upon the Achaeans. Many a brave soul did it send
    Line 8: hurrying down to Hades, and many a hero did it yield a prey to dogs and
    Line 9: vultures, for so were the counsels of Jove fulfilled from the day on
    ...
    

    Si bien esto est谩 perfectamente bien, hay una forma final que mencion茅 brevemente antes, que es menos expl铆cita pero un poco m谩s elegante, que prefiero mucho. Esta forma final de leer un archivo l铆nea por l铆nea incluye iterar sobre un objeto de archivo en un for bucle, asignando cada l铆nea a una variable especial llamada line. El fragmento de c贸digo anterior se puede replicar en el siguiente c贸digo, que se puede encontrar en el script de Python forlinein.py:

    filepath="Iliad.txt"
    with open(filepath) as fp:
       for cnt, line in enumerate(fp):
           print("Line {}: {}".format(cnt, line))
    

    En esta implementaci贸n, estamos aprovechando una funcionalidad de Python incorporada que nos permite iterar sobre el objeto de archivo impl铆citamente usando un for bucle en combinaci贸n con el uso del objeto iterable fp. Esto no solo es m谩s f谩cil de leer, sino que tambi茅n se necesitan menos l铆neas de c贸digo para escribir, lo que siempre es una buena pr谩ctica que vale la pena seguir.

    Una aplicaci贸n de ejemplo

    Ser铆a negligente escribir una aplicaci贸n sobre c贸mo consumir informaci贸n en un archivo de texto sin demostrar al menos un uso trivial de c贸mo usar una habilidad tan valiosa. Dicho esto, estar茅 demostrando una peque帽a aplicaci贸n que se puede encontrar en wordcount.py, que calcula la frecuencia de cada palabra presente en “La Il铆ada de Homero” usada en ejemplos anteriores. Esto crea una simple bolsa de palabras, que se usa com煤nmente en aplicaciones de PNL.

    import sys
    import os
    
    def main():
       filepath = sys.argv[1]
    
       if not os.path.isfile(filepath):
           print("File path {} does not exist. Exiting...".format(filepath))
           sys.exit()
      
       bag_of_words = {}
       with open(filepath) as fp:
           cnt = 0
           for line in fp:
               print("line {} contents {}".format(cnt, line))
               record_word_cnt(line.strip().split(' '), bag_of_words)
               cnt += 1
       sorted_words = order_bag_of_words(bag_of_words, desc=True)
       print("Most frequent 10 words {}".format(sorted_words[:10]))
      
    def order_bag_of_words(bag_of_words, desc=False):
       words = [(word, cnt) for word, cnt in bag_of_words.items()]
       return sorted(words, key=lambda x: x[1], reverse=desc)
    
    def record_word_cnt(words, bag_of_words):
        for word in words:
            if word != '':
                if word.lower() in bag_of_words:
                    bag_of_words[word.lower()] += 1
                else:
                    bag_of_words[word.lower()] = 1
    
    if __name__ == '__main__':
        main()
    

    El c贸digo anterior representa una secuencia de comandos de Python de l铆nea de comando que espera una ruta de archivo pasada como argumento. El script usa el os module para asegurarse de que la ruta del archivo pasada sea un archivo que existe en el disco. Si la ruta existe, cada l铆nea del archivo se lee y se pasa a una funci贸n llamada record_word_cnt como una lista de cadenas, delimit贸 los espacios entre palabras, as铆 como un diccionario llamado bag_of_words. los record_word_cnt La funci贸n cuenta cada instancia de cada palabra y la registra en la bag_of_words diccionario.

    Una vez le铆das y registradas todas las l铆neas del archivo en el bag_of_words diccionario, luego una llamada de funci贸n final a order_bag_of_words se llama, que devuelve una lista de tuplas en formato (palabras, recuento de palabras), ordenadas por recuento de palabras. La lista de tuplas devuelta se utiliza para imprimir las 10 palabras que aparecen con m谩s frecuencia.

    Conclusi贸n

    Entonces, en este art铆culo hemos explorado formas de leer un archivo de texto l铆nea por l铆nea de dos formas, incluida una forma que creo que es un poco m谩s Pythonic (esta es la segunda forma demostrada en forlinein.py). Para resumir, present茅 una aplicaci贸n trivial que es potencialmente 煤til para leer y preprocesar datos que podr铆an usarse para an谩lisis de texto o an谩lisis de sentimientos.

    Como siempre, espero sus comentarios y espero que puedan utilizar lo que se ha discutido para desarrollar aplicaciones interesantes y 煤tiles.

    Etiquetas:

    Deja una respuesta

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