Módulo 1 · Lección 45 de 59
Escribir ficheros de texto
Los cuatro modos de apertura, la diferencia entre borrar y añadir, y cómo dejar los datos guardados en disco sin perder nada.
Un programa que solo imprime en pantalla olvida todo al terminar. Guardar en un fichero es lo que convierte un ejercicio en una herramienta: un informe que puedes enviar, una lista que sobrevive al reinicio, un registro de lo que ha ido pasando.
Escribir se hace con el mismo open() de la lección anterior, pero con un segundo argumento que es donde está toda la miga: el modo. Elegir mal el modo es la forma más rápida de borrar un fichero sin querer.
Los cuatro modos#
with open("notas.txt", "w", encoding="utf-8") as fichero:
fichero.write("Hola\n")Ese "w" es el modo. Hay cuatro que necesitas conocer:
| Modo | Nombre | Si el fichero existe | Si no existe |
|---|---|---|---|
"r" | Lectura (por defecto) | Lo lee | FileNotFoundError |
"w" | Escritura | Borra todo el contenido | Lo crea |
"a" | Añadir | Escribe al final | Lo crea |
"x" | Creación exclusiva | FileExistsError | Lo crea |
Cuando no pones modo, Python usa "r". Por eso en la lección anterior bastaba con open("compra.txt", encoding="utf-8").
write() no añade el salto de línea#
Esta es la sorpresa del primer día. print pone un salto al final de cada llamada; write no pone nada:
with open("notas.txt", "w", encoding="utf-8") as fichero:
fichero.write("Primera línea")
fichero.write("Segunda línea")El fichero queda así:
Primera líneaSegunda líneaLos saltos de línea los pones tú, con \n:
with open("notas.txt", "w", encoding="utf-8") as fichero:
fichero.write("Primera línea\n")
fichero.write("Segunda línea\n")Primera línea
Segunda líneawrite() además devuelve el número de caracteres escritos, un dato que casi nunca usarás pero que explica por qué en la consola interactiva te aparece un número suelto después de escribir:
>>> fichero = open("prueba.txt", "w", encoding="utf-8")
>>> fichero.write("Hola\n")
5
>>> fichero.close()Escribir varias líneas#
Lo más habitual es tener una lista y volcarla. La forma clara es un for con una f-string que añade el salto:
participantes = ["Lucía", "Mateo", "Noa", "Hugo"]
with open("participantes.txt", "w", encoding="utf-8") as fichero:
for nombre in participantes:
fichero.write(f"{nombre}\n")Lucía
Mateo
Noa
HugoExiste también writelines(), que recibe una lista y la escribe entera. Su nombre engaña, porque tampoco añade saltos de línea:
participantes = ["Lucía", "Mateo", "Noa", "Hugo"]
with open("participantes.txt", "w", encoding="utf-8") as fichero:
fichero.writelines(participantes)LucíaMateoNoaHugoPara que sirva de algo hay que darle las líneas ya con su \n, cosa que se hace cómodamente con una comprensión:
participantes = ["Lucía", "Mateo", "Noa", "Hugo"]
lineas = [f"{nombre}\n" for nombre in participantes]
with open("participantes.txt", "w", encoding="utf-8") as fichero:
fichero.writelines(lineas)Lucía
Mateo
Noa
HugoOtra opción igual de válida es unir la lista con "\n".join(participantes) y hacer un solo write. Elige la que te resulte más legible; el for con f-string es la que menos dudas genera.
Añadir sin borrar: el modo "a"#
with open("diario.txt", "w", encoding="utf-8") as fichero:
fichero.write("Lunes: revisión del presupuesto\n")
with open("diario.txt", "a", encoding="utf-8") as fichero:
fichero.write("Martes: reunión con el cliente\n")
fichero.write("Miércoles: entrega de la primera versión\n")Lunes: revisión del presupuesto
Martes: reunión con el cliente
Miércoles: entrega de la primera versiónCon "a" el cursor se coloca al final del fichero nada más abrirlo, así que todo lo que escribas se suma a lo que ya había. Si el fichero no existía, lo crea. Es el modo de los registros y de cualquier cosa que vaya creciendo.
Crear sin pisar: el modo "x"#
"x" crea el fichero solo si no existe. Si existe, falla en vez de tocarlo:
with open("diario.txt", "x", encoding="utf-8") as fichero:
fichero.write("esto no llega a escribirse\n")Traceback (most recent call last):
File "/Users/erwin/curso-python/diario.py", line 1, in <module>
with open("diario.txt", "x", encoding="utf-8") as fichero:
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileExistsError: [Errno 17] File exists: 'diario.txt'Un registro de actividad#
Este es el ejemplo típico del modo "a": una función que anota lo que va pasando, con su fecha y su hora.
from datetime import datetime
RUTA_REGISTRO = "actividad.log"
def anotar(mensaje: str) -> None:
"""Añade una línea al registro con la fecha y la hora del momento."""
momento = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
with open(RUTA_REGISTRO, "a", encoding="utf-8") as fichero:
fichero.write(f"[{momento}] {mensaje}\n")
anotar("Programa iniciado")
anotar("Cargados 128 registros de clientes")
anotar("Aviso: 3 registros sin teléfono")
anotar("Programa terminado")El programa no imprime nada. Lo que deja en actividad.log es esto:
[19/09/2026 13:45:17] Programa iniciado
[19/09/2026 13:45:17] Cargados 128 registros de clientes
[19/09/2026 13:45:17] Aviso: 3 registros sin teléfono
[19/09/2026 13:45:17] Programa terminadoLa fecha y la hora serán las de tu ejecución, claro. Y si vuelves a lanzar el programa, las nuevas líneas se añaden debajo en vez de sustituir a las anteriores: ahí está la gracia del modo "a".
Dos detalles del diseño que conviene copiar. La ruta está en una constante en mayúsculas, arriba del todo, para cambiarla en un solo sitio. Y la función abre y cierra el fichero en cada llamada, que es más lento pero garantiza que, si el programa se cuelga, todo lo anotado hasta ese momento ya está en el disco. En un registro, eso es justo lo que quieres.