Leer y escribir ficheros es una de las operaciones más básicas de cualquier programa, pero Python tiene varios detalles que hay que conocer para no cometer los errores más habituales: olvidar cerrar el fichero, no especificar el encoding o elegir el modo incorrecto.
open() y el bloque with
Siempre usa with para abrir ficheros: garantiza el cierre aunque ocurra una excepción.
# BIEN: con bloque with
with open("datos.txt", "r", encoding="utf-8") as f:
contenido = f.read()
print(contenido)
# el fichero se cierra automáticamente al salir del with
# MAL: sin with el fichero podría quedarse abierto si hay excepción
f = open("datos.txt", "r")
contenido = f.read()
f.close()
Modos de apertura
# 'r' lectura (por defecto). Falla si el fichero no existe.
# 'w' escritura. Crea el fichero o lo trunca si ya existe.
# 'a' append. Crea el fichero o añade al final si ya existe.
# 'x' creación exclusiva. Falla si el fichero ya existe.
# 'rb' lectura binaria. Para imágenes, PDFs, etc.
# 'wb' escritura binaria.
with open("log.txt", "a", encoding="utf-8") as f:
f.write("nueva entrada de logn")
Métodos de lectura
# read(): todo el fichero como una cadena
with open("fichero.txt", encoding="utf-8") as f:
todo = f.read()
# readlines(): lista de líneas (incluye n)
with open("fichero.txt", encoding="utf-8") as f:
lineas = f.readlines()
# Iterar línea a línea (más eficiente en memoria)
with open("fichero.txt", encoding="utf-8") as f:
for linea in f:
print(linea.rstrip("n"))
# readline(): una línea cada vez
with open("fichero.txt", encoding="utf-8") as f:
primera = f.readline()
segunda = f.readline()
Ejemplo real: procesar un log de Nginx
from collections import Counter
def analizar_log(ruta_log):
"""Cuenta las IPs que más peticiones hacen en un log de acceso."""
ips = Counter()
with open(ruta_log, encoding="utf-8") as f:
for linea in f:
if linea.strip():
ip = linea.split()[0]
ips[ip] += 1
return ips.most_common(10)
# top_ips = analizar_log("/var/log/nginx/access.log")
pathlib.Path: la forma moderna
from pathlib import Path
ruta = Path("datos") / "usuarios.txt"
# read_text y write_text simplifican el open() para texto
contenido = ruta.read_text(encoding="utf-8")
ruta.write_text("nuevo contenidon", encoding="utf-8")
# Append con open()
with ruta.open("a", encoding="utf-8") as f:
f.write("línea adicionaln")
# Comprobar existencia, extensión, nombre
print(ruta.exists()) # True/False
print(ruta.suffix) # '.txt'
print(ruta.stem) # 'usuarios'
print(ruta.parent) # PosixPath('datos')
# Iterar ficheros de un directorio
for fichero in Path(".").glob("*.py"):
print(fichero.name)
Encoding: siempre explícito
# MAL: depende del encoding del sistema (distinto en Windows y Linux)
with open("fichero.txt") as f:
datos = f.read()
# BIEN: encoding siempre explícito
with open("fichero.txt", encoding="utf-8") as f:
datos = f.read()
# Para UTF-8 con BOM (común en ficheros de Windows)
with open("fichero.txt", encoding="utf-8-sig") as f:
datos = f.read()
La regla: siempre with, siempre encoding="utf-8" salvo razón explícita para usar otro. Para rutas de fichero en código nuevo, usa pathlib.Path en lugar de cadenas de texto.
