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

En este tutorial, leeremos un archivo línea por línea en Python con las funciones readline() y readlines(), así como un bucle for, a través de ejemplos prácticos.

Introducción

Una tarea común en la programación es abrir un archivo y analizar su contenido. ¿Qué hace cuando el archivo que intenta procesar es bastante grande, como varios GB de datos o más? La respuesta a este problema es leer fragmentos de un archivo a la vez, procesarlo y luego liberarlo de la memoria para que pueda procesar otro fragmento, hasta que se haya procesado todo el archivo masivo. Si bien depende de usted determinar un tamaño adecuado para los fragmentos de datos que está procesando, 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 que demuestran cómo leer archivos línea por línea. En caso de que quieras probar algunos de estos ejemplos por ti mismo, el código utilizado en este artículo se puede encontrar en el siguiente [repositorio de GitHub](https://github.com/amcquistan/Python-read-file-line- por línea).

Archivo básico IO en Python

Python es un excelente lenguaje de programación de propósito general, y tiene una serie de funciones de E/S de archivos muy útiles en su biblioteca estándar de funciones y módulos integrados.

La función integrada open() es lo que se usa para abrir un objeto de archivo con fines de lectura o escritura. Así es como puede usarlo para abrir un archivo:

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

Como se demostró anteriormente, la función open() acepta múltiples argumentos. Nos centraremos en dos argumentos, siendo el primero un parámetro de cadena posicional que representa la ruta al archivo que desea abrir. El segundo parámetro (opcional) también es una cadena y especifica el modo de interacción que desea utilizar en el objeto de archivo que devuelve la llamada a la función. Los modos más comunes se enumeran en la siguiente tabla, con el valor predeterminado 'r' para lectura:

Modo Descripción


r Abierto para leer texto sin formato w Abierto para escribir texto sin formato a Abre un archivo existente para agregar texto sin formato rb Abierto para leer datos binarios wb Abierto para escribir datos binarios

Una vez que haya escrito o leído todos los datos deseados en 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.

1
fp.close()

Nota: Siempre es una buena práctica cerrar un recurso de objeto de archivo, pero es una tarea que es fácil de olvidar.

Si bien siempre puede recordar llamar a close() en un objeto de archivo, hay una forma alternativa y más elegante de abrir un objeto de archivo y asegurarse de que el intérprete de Python se limpie después de su uso:

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

Simplemente usando la con palabra clave (introducida en Python 2.5) al código que usamos para abrir un objeto de archivo, Python hará algo similar al siguiente código. Esto asegura que no importa qué objeto de archivo se cierre después de su uso:

1
2
3
4
5
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 más Pythonic.

El objeto de archivo devuelto por la función open() tiene tres métodos comunes explícitos (read(), readline() y readlines()) para leer datos. El método read() lee todos los datos en una sola cadena. Esto es útil para archivos más pequeños en los que le gustaría manipular el texto en todo el archivo. Luego está readline(), que es una forma útil de leer solo líneas individuales, en cantidades incrementales 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.

Nota: Para el resto de este artículo trabajaremos con el texto del libro La "Ilíada de Homero", que se puede encontrar en [gutenberg.org](http://www.gutenberg. org), así como en el repositorio de GitHub donde se encuentra el código de este artículo.

Leer un archivo línea por línea en Python con readline()

Comencemos con el método readline(), que lee una sola línea, lo que requerirá que usemos un contador y lo incrementemos:

1
2
3
4
5
6
7
8
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

Este fragmento de código abre un objeto de archivo cuya referencia se almacena en fp, luego lee una línea a la vez llamando a readline() en ese objeto de archivo iterativamente en un bucle while. Luego simplemente imprime la línea en la consola.

Al ejecutar este código, debería ver algo como lo siguiente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
...
Line 567: exceedingly trifling. We have no remaining inscription earlier than the
Line 568: fortieth Olympiad, and the early inscriptions are rude and unskilfully
Line 569: executed; nor can we even assure ourselves whether Archilochus, Simonides
Line 570: of Amorgus, Kallinus, Tyrtaeus, Xanthus, and the other early elegiac and
Line 571: lyric poets, committed their compositions to writing, or at what time the
Line 572: practice of doing so became familiar. The first positive ground which
Line 573: authorizes us to presume the existence of a manuscript of Homer, is in the
Line 574: famous ordinance of Solon, with regard to the rhapsodies at the
Line 575: Panathenaea: but for what length of time previously manuscripts had
Line 576: existed, we are unable to say.
...

Sin embargo, este enfoque es crudo y explícito. Ciertamente no es muy pythonico. Podemos utilizar el método readlines() para hacer que este código sea mucho más breve.

Leer un archivo línea por línea con readlines()

El método readlines() lee todas las líneas y las almacena en una Lista. Luego podemos iterar sobre esa lista y usando enumerate(), hacer un índice para cada línea para nuestra conveniencia:

1
2
3
4
5
6
7
file = open('Iliad.txt', 'r')
lines = file.readlines()

for index, line in enumerate(lines):
    print("Line {}: {}".format(index, line.strip()))
    
file.close()

Esto resulta en:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
...
Line 160: INTRODUCTION.
Line 161:
Line 162:
Line 163: Scepticism is as much the result of knowledge, as knowledge is of
Line 164: scepticism. To be content with what we at present know, is, for the most
Line 165: part, to shut our ears against conviction; since, from the very gradual
Line 166: character of our education, we must continually forget, and emancipate
Line 167: ourselves from, knowledge previously acquired; we must set aside old
Line 168: notions and embrace fresh ones; and, as we learn, we must be daily
Line 169: unlearning something which it has cost us no small labour and anxiety to
Line 170: acquire.
...

Ahora, aunque mucho mejor, ni siquiera necesitamos llamar al método readlines() para lograr esta misma funcionalidad. Esta es la forma tradicional de leer un archivo línea por línea, pero hay una más moderna y más corta.

Leer un archivo línea por línea con un bucle for: el enfoque más pythonico {#leer un archivo línea por línea con un enfoque más pythonico para un bucle}

El ‘Archivo’ devuelto en sí mismo es iterable. No necesitamos extraer las líneas a través de readlines() en absoluto; podemos iterar el propio objeto devuelto. Esto también hace que sea fácil enumerar() para que podamos escribir el número de línea en cada declaración print().

Este es el enfoque más corto y más pythonico para resolver el problema, y ​​el enfoque preferido por la mayoría:

1
2
3
with open('Iliad.txt') as f:
    for index, line in enumerate(f):
        print("Line {}: {}".format(index, line.strip()))

Esto resulta en:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
...
Line 277: Mentes, from Leucadia, the modern Santa Maura, who evinced a knowledge and
Line 278: intelligence rarely found in those times, persuaded Melesigenes to close
Line 279: his school, and accompany him on his travels. He promised not only to pay
Line 280: his expenses, but to furnish him with a further stipend, urging, that,
Line 281: "While he was yet young, it was fitting that he should see with his own
Line 282: eyes the countries and cities which might hereafter be the subjects of his
Line 283: discourses." Melesigenes consented, and set out with his patron,
Line 284: "examining all the curiosities of the countries they visited, and
...

Aquí, estamos aprovechando las funcionalidades integradas de Python que nos permiten iterar sin esfuerzo sobre un objeto iterable, simplemente usando un bucle for. Si desea leer más sobre las funcionalidades integradas de Python en la iteración de objetos, lo tenemos cubierto:

Aplicaciones de lectura de archivos línea por línea

¿Cómo puedes usar esto en la práctica? La mayoría de las aplicaciones de PNL se ocupan de grandes corpus de datos. La mayoría de las veces, no será prudente leer todo el corpus en la memoria. Si bien es rudimentario, puede escribir una solución desde cero para contar la frecuencia de ciertas palabras, sin usar bibliotecas externas. Escribamos un script simple que se cargue en un archivo, lo lea línea por línea y cuente la frecuencia de las palabras, imprimiendo las 10 palabras más frecuentes y el número de sus ocurrencias:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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:
       for line in fp:
           record_word_cnt(line.strip().split(' '), bag_of_words)
   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 script usa el módulo os para asegurarse de que el archivo que estamos intentando leer realmente existe. Si es así, se lee línea por línea y cada línea se pasa a la función record_word_cnt(). Delimita los espacios entre palabras y agrega la palabra al diccionario - bag_of_words. Una vez que todas las líneas están registradas en el diccionario, lo ordenamos a través de order_bag_of_words() que devuelve una lista de tuplas en el formato (word, word_count), ordenadas por el número de palabras.

Finalmente, imprimimos las diez palabras más comunes.

Por lo general, para esto, crearía un Modelo Bolsa de Palabras, utilizando bibliotecas como NLTK, sin embargo, esta implementación será suficiente. Ejecutemos el script y proporcionemos nuestro Iliad.txt:

1
$ python app.py Iliad.txt

Esto resulta en:

1
Most frequent 10 words [('the', 15633), ('and', 6959), ('of', 5237), ('to', 4449), ('his', 3440), ('in', 3158), ('with', 2445), ('a', 2297), ('he', 1635), ('from', 1418)]

Si quieres leer más sobre PNL, tenemos una serie de guías sobre diversas tareas: Procesamiento del lenguaje natural en Python.

Conclusión

En este artículo, hemos explorado múltiples formas de leer un archivo línea por línea en Python, así como también hemos creado un modelo rudimentario Bag of Words para calcular la frecuencia de las palabras en un archivo determinado.

Licensed under CC BY-NC-SA 4.0