Archivos de texto en Python — teoría con un diario de entrenamientos, modos que no perdonan y saltos de línea escondidos (y el ‘w’ que se comió mi diario)

A principios de octubre empecé a apuntar mis entrenos en un archivo de texto: una línea por sesión, con la fecha, el deporte y los minutos. Llevaba tres sesiones y quise añadir la cuarta con un programita de cinco líneas. Lo ejecuté, abrí el diario y… solo estaba la sesión de hoy. Las otras tres habían desaparecido. Este es el programa, casi tal cual:

# Quería apuntar el entreno de hoy en mi diario... y me lo cargué
f = open('diario.txt', 'w', encoding='utf-8')     # así tenía el diario: tres sesiones
f.write('2026-10-05;carrera;35\n')
f.write('2026-10-06;bici;50\n')
f.write('2026-10-08;natación;40\n')
f.close()


def contar_sesiones(nombre):
    f = open(nombre, 'r', encoding='utf-8')
    n = 0
    for linea in f:          # una vuelta por línea del archivo
        n += 1
    f.close()
    return n


print('Sesiones antes:', contar_sesiones('diario.txt'))
f = open('diario.txt', 'w', encoding='utf-8')     # para "añadir" la de hoy... con 'w'
f.write('2026-10-09;carrera;30\n')
f.close()
print('Sesiones después:', contar_sesiones('diario.txt'))

Y esto es lo que sacó por pantalla:

Sesiones antes: 3
Sesiones después: 1

De 3 sesiones a 1. No hubo ningún error, ningún aviso, nada en rojo. El culpable es una sola letra: la 'w' del segundo open(). Abrir un archivo en modo escritura lo vacía en el mismo momento en que se abre, antes de escribir nada. Lo que yo quería era el modo 'a' (añadir). Hoy vamos a ver por qué pasa esto y todo lo demás que necesitas para leer y escribir archivos de texto sin sustos.

En este artículo aprendes qué es un archivo de texto, cómo se abre y se cierra, los modos 'r', 'w' y 'a', el esquema estándar de lectura con readline(), el recorrido con for, read(), las dos formas de escribir (print(..., file=f) y write()), la cláusula with, la codificación y el módulo os. Vienes de las expresiones regulares en Python, que te vendrán bien en los ejercicios, y cuando acabes tienes la práctica de archivos de texto. El ejemplo guía es mi diario de entrenamientos.

¿Qué es un archivo de texto?

Un archivo de texto es una secuencia de caracteres guardada fuera del programa, en el disco. Entre esos caracteres están los finales de línea, el carácter '\n'. Por eso también se puede ver como una secuencia de líneas: cada línea es una tira de caracteres que termina en '\n'. Los .txt, los .csv, los .html y tus propios .py son archivos de texto.

Este es mi diario, el archivo entrenos.txt que usaremos durante toda la teoría:

# diario de entrenamientos: fecha;deporte;minutos
2026-10-05;carrera;35
2026-10-06;bici;50

2026-10-08;natación;40
2026-10-09; carrera ;30
2026-10-11;yoga;veinte
2026-10-12;fútbol;90
2026-10-14;bici;45

Fíjate en que no es perfecto, y eso es a propósito: tiene un comentario arriba, una línea vacía (la cuarta), una línea con espacios de más alrededor de carrera (y, aunque aquí no se vea, dos espacios y un tabulador al final), y una sesión de yoga con veinte escrito en letras. Los archivos de verdad son así.

Un archivo vive fuera de tu programa. Lo crean, cambian y borran los programas, pero lo gestiona el sistema de gestión de archivos del sistema operativo. Por eso hablar con un archivo tiene tres etapas: abrir (conectar el programa con el archivo), transferir (leer o escribir) y cerrar (soltar la conexión y terminar las escrituras pendientes).

Abrir y cerrar un archivo

Se abre con la función open(), que recibe el nombre del archivo y devuelve un manejador: un objeto que representa la conexión. A partir de ahí trabajas con el manejador (f), no con el nombre.

# Las tres etapas: abrir, transferir y cerrar
f = open('entrenos.txt', 'r', encoding='utf-8')   # 1. abrir: f es el manejador del archivo
primera = f.readline()                            # 2. transferir: lee hasta el primer '\n', incluido
f.close()                                         # 3. cerrar: libera el archivo
print(primera, end='')    # la línea ya trae su '\n'
print(repr(primera))      # repr() enseña el '\n' que print() esconde
print(f.closed)           # True: el manejador ya está cerrado

Al ejecutarlo:

# diario de entrenamientos: fecha;deporte;minutos
'# diario de entrenamientos: fecha;deporte;minutos\n'
True

Vamos por partes:

  • open('entrenos.txt', 'r', encoding='utf-8'): el primer argumento es el nombre; si solo pones el nombre «corto», Python lo busca en la carpeta de trabajo (desde donde se ejecuta el programa). El segundo, 'r', es el modo lectura (es el modo por omisión, así que open('entrenos.txt') haría lo mismo). El tercero, encoding, dice cómo están codificados los caracteres; lo vemos más abajo.
  • f.readline() lee una línea: desde donde se quedó hasta el siguiente '\n', incluido.
  • f.close() cierra. Después, f.closed vale True.
  • La primera línea sale dos veces: con print(primera, end='') se ve como en el archivo; con repr(primera) se ve lo que hay de verdad, con el '\n' del final a la vista.

FileNotFoundError: abrir para leer algo que no existe

El modo 'r' exige que el archivo exista. Si te equivocas en el nombre (o estás en otra carpeta), revienta:

# 'r' exige que el archivo exista (el mío se llama entrenos.txt)
f = open('entrenamientos.txt', 'r', encoding='utf-8')

El mensaje es muy claro:

Traceback (most recent call last):
  File "no_existe.py", line 2, in <module>
    f = open('entrenamientos.txt', 'r', encoding='utf-8')
FileNotFoundError: [Errno 2] No such file or directory: 'entrenamientos.txt'

Ejecutar el programa desde otra carpeta. Si en VS Code o en la terminal la carpeta de trabajo no es la del archivo, open('entrenos.txt') no lo encuentra aunque «esté ahí al lado». Comprueba la ruta o usa os.path.exists() antes de abrir (lo ves al final).

Los modos ‘r’, ‘w’ y ‘a’

Los tres modos que vas a usar con archivos de texto:

ModoPara quéSi el archivo no existeSi ya existe
'r'leerFileNotFoundErrorlo lee desde el principio
'w'escribirlo crea vacíolo vacía al abrir
'a'añadirlo crea vacíoescribe al final, sin borrar

Míralos en acción sobre el mismo archivo, semana.txt:

# Los tres modos sobre el mismo archivo


def mostrar(nombre):
    f = open(nombre, 'r', encoding='utf-8')
    print(repr(f.read()))       # el archivo entero en una string, con los '\n' a la vista
    f.close()


f = open('semana.txt', 'w', encoding='utf-8')   # no existe: 'w' lo crea vacío
f.write('lunes;carrera;35\n')
f.close()
mostrar('semana.txt')

f = open('semana.txt', 'a', encoding='utf-8')   # 'a': el cursor empieza al final, no borra nada
f.write('martes;bici;50\n')
f.close()
mostrar('semana.txt')

f = open('semana.txt', 'w', encoding='utf-8')   # 'w' otra vez: vacía el archivo antes de escribir
f.write('miércoles;yoga;20\n')
f.close()
mostrar('semana.txt')

La función mostrar() lee el archivo entero con read() (lo vemos enseguida) y lo enseña con repr(). Esta es la salida, una línea por cada vez que se llama:

'lunes;carrera;35\n'
'lunes;carrera;35\nmartes;bici;50\n'
'miércoles;yoga;20\n'
  1. El primer 'w' crea semana.txt (no existía) y escribe el lunes.
  2. El 'a' coloca el cursor al final y escribe el martes detrás: ahora hay dos líneas.
  3. El segundo 'w' vacía el archivo al abrirlo y escribe el miércoles. El lunes y el martes ya no están.

Esto es exactamente lo que me pasó en el gancho, y el dibujo lo resume:

Modos r, w y a Tres columnas con el mismo archivo antes y después de abrirlo en modo r, w y a. Mismo archivo, tres modos de apertura open(‘semana.txt’, ‘r’) Antes lunes;carrera;35⏎ Después lunes;carrera;35⏎ cursor al principio Lee. No cambia nada Si no existe: FileNotFoundError open(‘semana.txt’, ‘w’) Antes lunes;carrera;35⏎ Después miércoles;yoga;20⏎ write(…) escribe Vacía el archivo al abrir Si no existe, lo crea open(‘semana.txt’, ‘a’) Antes lunes;carrera;35⏎ Después lunes;carrera;35⏎ martes;bici;50⏎ cursor al final Añade al final Si no existe, lo crea
Los tres modos sobre el mismo archivo: ‘r’ solo lee, ‘w’ lo vacía antes de escribir y ‘a’ escribe detrás de lo que había.

Antes de escribir open(..., 'w'), pregúntate: «¿me da igual perder lo que haya en este archivo?». Si la respuesta es no, quieres 'a'. El 'w' es para archivos que se rehacen enteros cada vez, como un informe.

El esquema estándar de lectura con readline()

readline() lee una línea cada vez que la llamas. Cuando ya no quedan líneas, devuelve la string vacía ''. Con eso se monta el esquema clásico de recorrido «hasta que se cumpla una condición»:

# Esquema estándar de lectura: readline() hasta que devuelve ''


def mostrar(nombre):
    f = open(nombre, 'r', encoding='utf-8')
    linea = f.readline()          # intento leer la primera línea
    while linea != '':            # '' solo llega al final; una línea vacía del archivo es '\n'
        print(linea, end='')      # la línea ya trae su '\n': end='' evita una línea en blanco extra
        linea = f.readline()      # la siguiente (o '' si se acabó)
    f.close()


mostrar('entrenos.txt')

La salida reproduce el archivo tal cual:

# diario de entrenamientos: fecha;deporte;minutos
2026-10-05;carrera;35
2026-10-06;bici;50

2026-10-08;natación;40
2026-10-09; carrera ;30  	
2026-10-11;yoga;veinte
2026-10-12;fútbol;90
2026-10-14;bici;45

El orden es: abrir, intentar leer la primera línea, y mientras lo leído no sea '', procesar y leer la siguiente. Así va con las cuatro primeras vueltas del bucle:

Vueltalinea antes de procesar¿linea != ''?Qué se imprime
1'# diario de entrenamientos: fecha;deporte;minutos\n'síel comentario
2'2026-10-05;carrera;35\n'síla primera sesión
3'2026-10-06;bici;50\n'síla segunda
4'\n'síuna línea en blanco
…………
10''nonada: se sale del bucle

Fíjate en la vuelta 4. La línea vacía del archivo no es la string vacía: es '\n', que tiene un carácter. Por eso el bucle no se para ahí. La string vacía '' solo llega cuando ya no hay nada más que leer.

readline paso a paso Un archivo de tres líneas, la segunda vacía, y lo que devuelve cada una de las cuatro llamadas a readline. Cada readline() avanza el cursor una línea El archivo Lo que devuelve readline() 2026-10-05;carrera;35⏎ 1. ‘2026-10-05;carrera;35\n’ una línea con su salto ⏎ 2. ‘\n’ línea vacía: NO es la string vacía 2026-10-06;bici;50⏎ 3. ‘2026-10-06;bici;50\n’ la última línea fin del archivo 4. » fin del archivo: el while se para
Una línea vacía del archivo se lee como ‘\n’; la string vacía » solo aparece cuando ya no queda nada que leer.

¿Por qué end=»?

Porque la línea ya trae su '\n', y print() añade otro por su cuenta. Mira qué pasa con un print(linea) a secas sobre tres.txt, un archivo de tres sesiones:

# El mismo esquema con print(linea) a secas
f = open('tres.txt', 'r', encoding='utf-8')
linea = f.readline()
while linea != '':
    print(linea)          # el '\n' de la línea + el '\n' que añade print = línea en blanco
    linea = f.readline()
f.close()

Sale una línea en blanco detrás de cada sesión:

2026-10-05;carrera;35

2026-10-06;bici;50

2026-10-08;natación;40

Tienes dos arreglos: print(linea, end='') (no añadas nada) o quitar el salto antes de imprimir con linea.rstrip(). rstrip() quita los '\n', espacios y tabuladores del final; strip() los de los dos extremos; lstrip(), los del principio.

repr(): ver lo que hay de verdad

Cuando un archivo se porta raro, repr() es tu lupa: enseña cada línea como la escribirías en Python, con comillas, '\n' y tabuladores visibles.

# Ver lo que hay de verdad en cada línea
f = open('entrenos.txt', 'r', encoding='utf-8')
linea = f.readline()
while linea != '':
    print(repr(linea))    # comillas, '\n', espacios y tabuladores a la vista
    linea = f.readline()
print(repr(f.readline()))  # ya en el final: readline() devuelve la string vacía
f.close()

Ahora se ve todo:

'# diario de entrenamientos: fecha;deporte;minutos\n'
'2026-10-05;carrera;35\n'
'2026-10-06;bici;50\n'
'\n'
'2026-10-08;natación;40\n'
'2026-10-09; carrera ;30  \t\n'
'2026-10-11;yoga;veinte\n'
'2026-10-12;fútbol;90\n'
'2026-10-14;bici;45\n'
''

Ahí están los dos espacios y el \t de la sexta línea, que con print() eran invisibles, y la última llamada a readline(), ya en el final, devolviendo ''.

Escribir line = file.readline().rstrip() para leer «ya limpio». Funciona… hasta que el archivo tiene una línea vacía: '\n'.rstrip() es '', y el while cree que el archivo se ha terminado. Lo verás en directo en los ejercicios. Limpia una copia dentro del bucle y deja que la condición mire la línea original.

El archivo como secuencia de líneas: for

Un archivo abierto en lectura se puede recorrer con for, igual que una lista: cada vuelta te da una línea. Es el «esquema general de recorrido» y es la forma más cómoda:

# El archivo como secuencia de líneas: for, sin readline()
f = open('entrenos.txt', 'r', encoding='utf-8')
for numero, linea in enumerate(f, start=1):   # numera las líneas desde 1, como un editor
    linea = linea.strip()                      # fuera '\n' y espacios de los dos extremos
    if linea == '' or linea.startswith('#'):   # ni líneas vacías ni comentarios
        continue
    fecha, deporte, minutos = linea.split(';') # tres campos separados por ';'
    print(numero, fecha, deporte.strip(), minutos)   # strip(): ' carrera ' tiene espacios dentro del campo
f.close()

Con enumerate(f, start=1) cada línea viene con su número, como en el editor:

2 2026-10-05 carrera 35
3 2026-10-06 bici 50
5 2026-10-08 natación 40
6 2026-10-09 carrera 30
7 2026-10-11 yoga veinte
8 2026-10-12 fútbol 90
9 2026-10-14 bici 45
  • linea.strip() quita el '\n' y los espacios y tabuladores de los dos extremos de la línea.
  • if linea == '' or linea.startswith('#'): continue salta la línea vacía (que tras el strip() sí es '') y el comentario. Por eso faltan los números 1 y 4.
  • linea.split(';') corta por el separador y da tres campos. Pero strip() solo limpió los extremos de la línea, no los de cada campo: ' carrera ' sigue con sus espacios, y por eso hay un deporte.strip().
  • La sesión de yoga sale con veinte. De momento solo lo imprimimos; si intentáramos int('veinte'), saltaría un ValueError. En la práctica lo tratamos bien.

Un manejador se recorre una sola vez

El cursor avanza al leer y no vuelve solo al principio. Si recorres el mismo f dos veces, la segunda no encuentra nada:

# Recorrer el mismo manejador dos veces
f = open('entrenos.txt', encoding='utf-8')   # sin modo: 'r' por omisión
primera = 0
for linea in f:
    primera += 1
segunda = 0
for linea in f:          # el cursor ya está al final: no queda nada que leer
    segunda += 1
f.close()
print('Primera vuelta:', primera, 'líneas')
print('Segunda vuelta:', segunda, 'líneas')

El resultado lo dice todo:

Primera vuelta: 9 líneas
Segunda vuelta: 0 líneas

Las 9 líneas del archivo se leen en la primera vuelta y la segunda empieza con el cursor ya al final. Si necesitas recorrerlo otra vez, ciérralo y vuelve a abrirlo (o guarda las líneas en una lista la primera vez).

Leer todo de una vez: read()

read() sin argumentos devuelve todo el archivo en una sola string. Viene bien con archivos pequeños:

# read(): todo el archivo en una sola string
f = open('tres.txt', 'r', encoding='utf-8')
texto = f.read()
f.close()
print(len(texto))           # caracteres, contando cada '\n'
print(texto.count('\n'))    # cuántas líneas acaban en '\n'
print(texto.splitlines())   # lista de líneas, ya sin '\n'

Con tres.txt:

64
3
['2026-10-05;carrera;35', '2026-10-06;bici;50', '2026-10-08;natación;40']

Comprobémoslo a mano. Las líneas son 2026-10-05;carrera;35 (10 + 1 + 7 + 1 + 2 = 21 caracteres), 2026-10-06;bici;50 (10 + 1 + 4 + 1 + 2 = 18) y 2026-10-08;natación;40 (10 + 1 + 8 + 1 + 2 = 22). Más los tres '\n': 21 + 18 + 22 + 3 = 64. Y splitlines() te da la lista de líneas ya sin el salto.

read(n): de n en n caracteres

Con un número, read(n) lee como mucho n caracteres y el cursor avanza. Puedes mezclarlo con readline():

# read(n) lee n caracteres y el cursor avanza
f = open('tres.txt', 'r', encoding='utf-8')
print(repr(f.read(4)))      # los 4 primeros caracteres
print(repr(f.read(7)))      # los 7 siguientes: sigue donde se quedó
print(repr(f.readline()))   # el resto de esa línea
print(repr(f.read()))       # todo lo que queda
print(repr(f.read()))       # nada: ya estamos al final
f.close()

Cada llamada sigue donde se quedó la anterior:

'2026'
'-10-05;'
'carrera;35\n'
'2026-10-06;bici;50\n2026-10-08;natación;40\n'
''

read(4) se lleva 2026; read(7) sigue con -10-05; (7 caracteres: -, 1, 0, -, 0, 5, ;); readline() termina esa línea; read() se lleva el resto, y un último read() ya no encuentra nada y devuelve '', igual que readline().

¿Cuándo uso cada forma de leer?

Quiero…UsoPor qué
procesar línea a líneafor linea in flo más claro; no hay que llamar a nada
parar en cuanto encuentre algoreadline() + whilecontrolas tú cuándo seguir
el texto entero (contar, buscar con regex)read()una sola string; solo para archivos pequeños
un número exacto de caracteresread(n)formatos de ancho fijo, cabeceras

Escribir: print(…, file=f) y write()

Para escribir, abre en 'w' o 'a'. Tienes dos herramientas, y se diferencian justo en lo que suele fallar: el salto de línea y el tipo.

# Escribir: print(..., file=f) frente a f.write()
sesiones = [('2026-10-15', 'carrera', 40), ('2026-10-16', 'yoga', 25)]

f = open('con_print.txt', 'w', encoding='utf-8')
for fecha, deporte, minutos in sesiones:
    print(fecha, deporte, minutos, sep=';', file=f)   # convierte a str y añade el '\n' solo
f.close()

f = open('con_write.txt', 'w', encoding='utf-8')
for fecha, deporte, minutos in sesiones:
    n = f.write(f'{fecha};{deporte};{minutos}\n')    # escribe EXACTAMENTE esta string
    print('write ha escrito', n, 'caracteres')        # write devuelve cuántos caracteres escribió
f.close()

for nombre in ['con_print.txt', 'con_write.txt']:
    f = open(nombre, 'r', encoding='utf-8')
    print(nombre, repr(f.read()))
    f.close()

Los dos archivos acaban igual, pero no por el mismo camino:

write ha escrito 22 caracteres
write ha escrito 19 caracteres
con_print.txt '2026-10-15;carrera;40\n2026-10-16;yoga;25\n'
con_write.txt '2026-10-15;carrera;40\n2026-10-16;yoga;25\n'
  • print(fecha, deporte, minutos, sep=';', file=f) hace lo de siempre pero en el archivo: convierte cada cosa a texto (el 40 es un int y no pasa nada), las separa con sep y **añade el '\n' al final**.
  • f.write(texto) escribe exactamente la string que le das. Nada más. Ni separadores, ni salto, ni conversiones. Devuelve cuántos caracteres escribió: 22 para '2026-10-15;carrera;40\n' (10 + 1 + 7 + 1 + 2 + 1 = 22) y 19 para la de yoga (10 + 1 + 4 + 1 + 2 + 1 = 19).
print frente a write La misma sesión escrita con print y con write, y el TypeError al pasar un entero a write. Dos formas de escribir la misma línea print(fecha, deporte, minutos, sep=’;’, file=f) 2026-10-15 ; sep carrera ; 40 str(40) ⏎ lo añade print f.write(f'{fecha};{deporte};{minutos}\n’) 2026-10-15 ; carrera ; 40 ⏎ lo pones tú f.write(minutos) # 40, un int TypeError: write() argument must be str, not int
print() convierte a texto, separa con sep y añade el salto; write() escribe exactamente la string que recibe, y solo strings.

Si se te olvida el '\n' con write(), las sesiones se pegan:

# write() sin '\n': todo acaba en la misma línea
f = open('pegado.txt', 'w', encoding='utf-8')
f.write('2026-10-15;carrera;40')
f.write('2026-10-16;yoga;25')
f.close()
f = open('pegado.txt', 'r', encoding='utf-8')
print(repr(f.read()))
f.close()

Una sola línea con las dos sesiones, sin salto entre 40 y 2026:

'2026-10-15;carrera;402026-10-16;yoga;25'

f.write(minutos) con un número. write() solo acepta strings y lanza TypeError: write() argument must be str, not int. Conviértelo con str(minutos) o, mejor, monta la línea con una f-string.

En el esquema de escritura, el bucle recorre el origen de los datos (la lista, otro archivo…), no el archivo que estás escribiendo, que es solo el destino.

La cláusula with

Olvidarse del close() es facilísimo, y si el programa falla antes de llegar a él, el archivo se queda abierto (y lo escrito puede no haberse guardado del todo). with lo arregla: abre el archivo, te da el manejador y lo cierra solo al salir del bloque, pase lo que pase dentro.

# with cierra el archivo al salir del bloque, pase lo que pase
total = 0
with open('entrenos.txt', 'r', encoding='utf-8') as f:
    for linea in f:
        partes = linea.strip().split(';')
        if len(partes) == 3 and partes[2].isdigit():   # solo líneas con minutos válidos
            total += int(partes[2])
    print('¿Cerrado dentro del with?', f.closed)
print('¿Cerrado fuera del with?', f.closed)
print('Minutos válidos:', total)

Mira el estado del manejador dentro y fuera:

¿Cerrado dentro del with? False
¿Cerrado fuera del with? True
Minutos válidos: 290

Dentro del with, f.closed es False; en cuanto la indentación vuelve atrás, es True, sin haber escrito ningún close(). Los 290 minutos salen de 35 + 50 + 40 + 30 + 90 + 45 = 290: el comentario y la línea vacía no tienen tres campos, y 'veinte'.isdigit() es False. La línea de los espacios sí cuenta, porque strip() deja '30' limpio.

Desde hoy, abre siempre con with. Puedes abrir dos archivos en el mismo: with open('a.txt') as fin, open('b.txt', 'w') as fout:. Lo usaremos para copiar y filtrar.

La codificación: encoding=’utf-8′

Por dentro, un archivo son bytes. Un archivo de texto guarda caracteres codificados según una norma, y para leerlos bien hay que usar la misma norma con la que se escribieron. Hoy lo normal es UTF-8, donde la á ocupa dos bytes. Si lees con otra codificación, esos dos bytes se interpretan como dos caracteres distintos:

# La misma línea leída con la codificación buena y con otra
with open('ruta.txt', 'w', encoding='utf-8') as f:
    f.write('Ruta en bici hasta Gáldar\n')

with open('ruta.txt', 'r', encoding='utf-8') as f:
    print(repr(f.read()))    # la misma codificación con la que se escribió
with open('ruta.txt', 'r', encoding='latin-1') as f:
    print(repr(f.read()))    # otra codificación: la 'á' (2 bytes en UTF-8) sale como 2 caracteres raros

La misma línea, leída de dos formas:

'Ruta en bici hasta Gáldar\n'
'Ruta en bici hasta Gáldar\n'

Ese Gáldar es el clásico texto «roto» que habrás visto en alguna web. En muchos Windows la codificación por defecto no ha sido UTF-8, así que un open('x.txt') sin encoding puede leer mal los acentos de un archivo que en otro ordenador se ve perfecto.

Pon siempre encoding='utf-8' al leer y al escribir. Es un argumento más y te ahorra el problema en todos los ordenadores.

El módulo os: rutas, carpetas y archivos

El módulo os te deja hablar con el sistema operativo: comprobar si algo existe, crear carpetas, unir rutas, listar y borrar. Hay que importarlo con import os.

# El módulo os: rutas, carpetas y archivos
import os

print(os.path.exists('entrenos.txt'))      # True: está en la carpeta de trabajo
print(os.path.exists('entrenos2025.txt'))  # False: así se comprueba ANTES de abrir con 'r'
os.makedirs('copias/2026', exist_ok=True)  # crea también la carpeta intermedia; exist_ok: sin error si ya existe
ruta = os.path.join('copias', '2026', 'octubre.txt')   # une las partes con el separador del sistema
print(ruta)
with open(ruta, 'w', encoding='utf-8') as f:
    f.write('copia de prueba\n')
print(os.listdir(os.path.join('copias', '2026')))     # lo que hay dentro de la carpeta
print(os.path.isdir('copias'), os.path.isdir(ruta))   # carpeta sí, archivo no
os.remove(ruta)                                       # borra el archivo (sin papelera)
print(os.listdir(os.path.join('copias', '2026')))

Paso a paso:

True
False
copias/2026/octubre.txt
['octubre.txt']
True False
[]
  • os.path.exists(ruta): True o False. Es la forma de evitar el FileNotFoundError antes de abrir en 'r' (o de no machacar algo en 'w').
  • os.makedirs('copias/2026', exist_ok=True): crea la carpeta y las intermedias que falten. Con exist_ok=True no da error si ya existen (os.mkdir crea solo una y falla si ya está).
  • os.path.join('copias', '2026', 'octubre.txt'): une las partes con el separador del sistema. Aquí, en Linux, salió copias/2026/octubre.txt; en Windows saldría copias\2026\octubre.txt. Por eso no se escriben las barras a mano.
  • os.listdir(carpeta): lista lo que hay dentro. os.path.isdir(): ¿es una carpeta?
  • os.remove(ruta): borra el archivo, sin papelera. Después, la lista sale vacía.

Hay más: os.getcwd() te dice la carpeta de trabajo (útil para el error de antes), os.chdir() la cambia y os.rmdir() borra una carpeta vacía.

Errores típicos con archivos

Abrir con 'w' para «añadir». Vacía el archivo al abrir. Para añadir, 'a'.

print(linea) con líneas leídas del archivo: salen líneas en blanco entre medias. Usa end='' o rstrip().

Recorrer dos veces el mismo manejador. La segunda vuelta no lee nada: el cursor está al final.

write() sin '\n' (todo en una línea) o con un número (TypeError).

Olvidar close(). Con with no puede pasar.

Míralo paso a paso en Python Tutor

Python Tutor ejecuta tu código línea a línea y dibuja las variables. Elige Python 3. Tiene una limitación: no abre archivos de tu disco. Así que simulamos el archivo con una string y iter(...): cada next(f, '') hace de readline() y devuelve '' cuando no quedan líneas.

# Python Tutor no abre archivos de tu disco: el "archivo" es una string con sus '\n'
texto = '2026-10-05;carrera;35\n\n2026-10-06;bici;50\n'
f = iter(texto.splitlines(keepends=True))   # cada next() da una línea con su '\n', como readline()
total = 0
linea = next(f, '')          # '' cuando ya no quedan líneas, igual que readline()
while linea != '':
    limpia = linea.strip()   # limpio una copia; la condición sigue mirando la línea original
    if limpia != '':
        fecha, deporte, minutos = limpia.split(';')
        total += int(minutos)
    linea = next(f, '')
print('Total:', total, 'min')

La salida que verás al final:

Total: 85 min

Qué observar:

  1. En el paso en que se crea f, mira la lista de líneas: cada una **lleva su '\n'**, y la segunda es solo '\n'.
  2. En la segunda vuelta, linea vale '\n': la condición del while es True (no es ''), pero limpia vale '' y el if se la salta.
  3. Mira cómo total pasa de 0 a 35 y de 35 a 85 (35 + 50 = 85): solo cambia en las vueltas con sesión.
  4. En la última llamada, next(f, '') devuelve '' y el bucle termina. Es exactamente lo que hace readline() al final del archivo.

Resumen

ConceptoLo esencial
Archivo de textosecuencia de caracteres o de líneas acabadas en '\n'
open(nombre, modo, encoding='utf-8')devuelve el manejador; luego close()
'r' / 'w' / 'a'leer (debe existir) / escribir (vacía) / añadir al final
readline()una línea con su '\n'; '' al final del archivo
for linea in frecorrido línea a línea; una sola vez por apertura
read() / read(n)todo en una string / n caracteres
print(..., file=f)convierte, separa y añade '\n'
f.write(s)escribe solo strings y exactamente lo que le das
withcierra el archivo solo
osexists, makedirs, join, listdir, remove

Tres ideas para llevarte:

  1. Una línea vacía es '\n', no ''. La string vacía solo significa «se acabó el archivo».
  2. 'w' borra, 'a' añade. Elige el modo pensando en lo que ya hay en el archivo.
  3. print() pone el salto por ti; write() no pone nada. Y abre siempre con with y encoding='utf-8'.

Ahora toca usarlo de verdad: en la práctica de archivos de texto leemos el diario completo con sus líneas raras, apuntamos sesiones sin borrar nada y escribimos un informe del mes en un archivo nuevo. Y con este tema cierras el temario de FP1: ¡enhorabuena!

Siguiente paso: prácticaArchivos de texto en Python — práctica con el resumen del diario, sesiones que se apuntan sin borrar nada y un informe del mes (y la sesión que se pegó a la del día anterior)

¿Te ha servido este artículo? Así puedes apoyar SergioLearns →

Sigue aprendiendo

2 comentarios

Deja una respuesta

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