Archivos en Python: leer y escribir texto, CSV y JSON
- Por qué los archivos son la memoria permanente del programa
- open() y with: abrir archivos de forma segura
- Leer archivos de texto: read, readline e iteración
- Escribir y añadir contenido a archivos
- pathlib: rutas como objetos, no como strings
- CSV: leer y escribir datos tabulares
- JSON: configuraciones y APIs
- Programa completo: agenda en JSON
- Errores clásicos con archivos
- Preguntas frecuentes
Todo lo que tu programa calcula desaparece cuando el proceso termina. Si quieres que los datos sobrevivan entre ejecuciones — una configuración, los resultados de un análisis, un registro de actividad — necesitas guardarlos en algún sitio. Los archivos son la forma más directa: están ahí cuando el programa termina, cuando reinicias el ordenador y cuando abres el resultado en otro programa.
Python tiene soporte nativo para tres formatos que cubren el 90% de los casos: texto plano, CSV y JSON. No necesitas instalar nada.
📂 open() y with: abrir archivos de forma segura
La función open() devuelve un objeto archivo. Lo más importante: siempre usar with, que garantiza que el archivo se cierra aunque ocurra una excepción.
# La forma correcta — siempre
with open("notas.txt", "r", encoding="utf-8") as f:
contenido = f.read()
# Aquí f ya está cerrado automáticamente
# Sin with (funciona pero es propenso a errores):
f = open("notas.txt", "r", encoding="utf-8")
try:
contenido = f.read()
finally:
f.close() # hay que acordarse siempre
# Verificar si un archivo existe antes de abrirlo:
from pathlib import Path
ruta = Path("config.json")
if ruta.exists():
with open(ruta, encoding="utf-8") as f:
datos = f.read()
else:
print("No hay configuración, usando valores por defecto.")
📖 Leer archivos de texto
## read() — todo el contenido como un string
with open("poema.txt", encoding="utf-8") as f:
texto = f.read()
print(texto)
print(f"Total: {len(texto)} caracteres")
## readline() — una línea cada vez
with open("poema.txt", encoding="utf-8") as f:
primera = f.readline() # "En el principio era el Verbo\n"
segunda = f.readline() # siguiente línea
print(primera.strip())
## Iteración directa — la más eficiente en memoria
with open("accesos.log", encoding="utf-8") as f:
for linea in f:
linea = linea.rstrip("\n") # quitar salto de línea
if "ERROR" in linea:
print(linea)
## readlines() — lista de todas las líneas
with open("datos.txt", encoding="utf-8") as f:
lineas = f.readlines()
# ['línea 1\n', 'línea 2\n', 'línea 3\n']
# Limpiar los saltos de línea de golpe:
lineas = [l.rstrip() for l in lineas]
✏️ Escribir y añadir contenido
## write() — modo "w" crea o sobreescribe
with open("informe.txt", "w", encoding="utf-8") as f:
f.write("Informe de ventas\n")
f.write("=================\n")
f.write(f"Total: {1234} €\n")
## writelines() — escribe una lista (sin añadir \n automático)
lineas = ["Producto A: 100\n", "Producto B: 200\n"]
with open("productos.txt", "w", encoding="utf-8") as f:
f.writelines(lineas)
## Modo "a" — append: añade sin borrar lo anterior
import datetime
with open("app.log", "a", encoding="utf-8") as f:
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
f.write(f"[{timestamp}] Aplicación iniciada\n")
## Modo "x" — creación exclusiva (error si ya existe)
try:
with open("nuevo_archivo.txt", "x", encoding="utf-8") as f:
f.write("Creado por primera vez\n")
except FileExistsError:
print("El archivo ya existía, no se sobreescribió.")
🗂️ pathlib: rutas como objetos
pathlib.Path es la forma moderna de trabajar con rutas. En lugar de strings y os.path, tienes un objeto con métodos propios que funciona igual en Windows, macOS y Linux.
from pathlib import Path
# Crear rutas (/ funciona en todos los sistemas operativos)
base = Path("datos")
archivo = base / "usuarios" / "lista.csv"
# En Windows: datos\usuarios\lista.csv
# En Unix: datos/usuarios/lista.csv
# Propiedades útiles
p = Path("docs/informe_2024.pdf")
p.name # "informe_2024.pdf"
p.stem # "informe_2024" (sin extensión)
p.suffix # ".pdf"
p.parent # Path("docs")
p.exists() # True / False
p.is_file() # True si es archivo
p.is_dir() # True si es directorio
# Leer y escribir directamente (sin open())
texto = Path("notas.txt").read_text(encoding="utf-8")
Path("salida.txt").write_text("Contenido", encoding="utf-8")
# Crear directorio (con padres si hace falta)
Path("datos/cache/tmp").mkdir(parents=True, exist_ok=True)
# Listar archivos de un directorio
for archivo in Path("datos").glob("*.csv"):
print(archivo.name)
# Ruta absoluta
print(Path("config.json").resolve())
# /home/ana/proyecto/config.json
📊 CSV: datos tabulares
El módulo csv viene con Python. DictReader y DictWriter son las formas más cómodas: usan la primera fila como cabecera y trabajan con diccionarios por fila.
import csv
## Leer — DictReader (cabecera automática)
with open("empleados.csv", encoding="utf-8") as f:
lector = csv.DictReader(f)
for fila in lector:
print(f"{fila['nombre']} — {fila['departamento']}")
## Leer — csv.reader (sin cabecera, devuelve listas)
with open("numeros.csv", encoding="utf-8") as f:
lector = csv.reader(f)
next(lector) # saltar cabecera manualmente
for fila in lector:
print(fila[0], fila[1])
## Escribir — DictWriter
empleados = [
{"nombre": "Ana", "departamento": "IT", "salario": 38000},
{"nombre": "Luis", "departamento": "Ventas", "salario": 32000},
]
campos = ["nombre", "departamento", "salario"]
with open("salida.csv", "w", newline="", encoding="utf-8") as f:
escritor = csv.DictWriter(f, fieldnames=campos)
escritor.writeheader() # escribe la fila de cabecera
escritor.writerows(empleados)
## CSV con separador punto y coma (formato europeo)
with open("europeo.csv", encoding="utf-8") as f:
lector = csv.DictReader(f, delimiter=";")
for fila in lector:
print(fila)
🔧 JSON: configuraciones y APIs
JSON es el formato estándar para configuraciones, respuestas de APIs REST y persistencia ligera de datos. Python lo convierte directamente a y desde diccionarios, listas, strings, números y booleanos.
import json
## Leer JSON desde archivo
with open("config.json", encoding="utf-8") as f:
config = json.load(f)
print(config["base_de_datos"]["host"])
## Escribir JSON en archivo
config = {
"version": "1.2",
"base_de_datos": {
"host": "localhost",
"puerto": 5432,
"nombre": "mi_app"
},
"debug": False,
"etiquetas": ["produccion", "v2"]
}
with open("config.json", "w", encoding="utf-8") as f:
json.dump(config, f, indent=4, ensure_ascii=False)
## String ↔ dict (sin archivo — útil para APIs)
texto_json = '{"nombre": "Ana", "edad": 28}'
datos = json.loads(texto_json) # str → dict
print(datos["nombre"]) # "Ana"
cadena = json.dumps(datos, indent=2) # dict → str
print(cadena)
## JSON con tipos Python especiales
from datetime import date
import json
# Las fechas no son JSON nativas — necesitas convertirlas:
class EncoderFechas(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, date):
return obj.isoformat() # "2026-03-06"
return super().default(obj)
datos = {"nombre": "Ana", "nacimiento": date(1995, 4, 12)}
json.dumps(datos, cls=EncoderFechas)
# '{"nombre": "Ana", "nacimiento": "1995-04-12"}'
🛠️ Programa completo: agenda en JSON
import json
from pathlib import Path
from datetime import datetime
ARCHIVO = Path("agenda.json")
def cargar_agenda():
if not ARCHIVO.exists():
return []
try:
return json.loads(ARCHIVO.read_text(encoding="utf-8"))
except json.JSONDecodeError:
print("⚠ El archivo de agenda está corrupto. Empezando desde cero.")
return []
def guardar_agenda(contactos):
ARCHIVO.write_text(
json.dumps(contactos, indent=2, ensure_ascii=False),
encoding="utf-8"
)
def mostrar_contactos(contactos):
if not contactos:
print(" (agenda vacía)")
return
for i, c in enumerate(sorted(contactos, key=lambda x: x["nombre"]), 1):
print(f" {i:>2}. {c['nombre']:<25} {c.get('telefono','—')}")
def buscar(contactos, texto):
texto = texto.lower()
return [c for c in contactos
if texto in c["nombre"].lower()
or texto in c.get("email","").lower()]
# ── Programa principal ──
contactos = cargar_agenda()
print(f"Agenda cargada: {len(contactos)} contacto(s)\n")
while True:
print("1. Ver todos 2. Buscar 3. Añadir 4. Eliminar 5. Salir")
op = input("Opción: ").strip()
if op == "1":
mostrar_contactos(contactos)
elif op == "2":
texto = input(" Buscar: ").strip()
resultado = buscar(contactos, texto)
print(f" {len(resultado)} resultado(s):")
mostrar_contactos(resultado)
elif op == "3":
nombre = input(" Nombre: ").strip()
telefono = input(" Teléfono: ").strip()
email = input(" Email: ").strip()
contactos.append({
"nombre": nombre,
"telefono": telefono,
"email": email,
"creado": datetime.now().isoformat()
})
guardar_agenda(contactos)
print(f" ✓ {nombre} añadido y guardado.")
elif op == "4":
mostrar_contactos(contactos)
try:
idx = int(input(" Nº a eliminar: ")) - 1
eliminado = sorted(contactos, key=lambda x: x["nombre"])[idx]
contactos.remove(eliminado)
guardar_agenda(contactos)
print(f" ✓ {eliminado['nombre']} eliminado.")
except (ValueError, IndexError):
print(" Número no válido.")
elif op == "5":
print("Hasta luego.")
break
🐛 Errores clásicos con archivos
1. Olvidar encoding y romper las tildes
# ❌ Depende del sistema — falla en Windows con tildes
with open("texto.txt") as f:
contenido = f.read()
# ✅ Siempre explícito
with open("texto.txt", encoding="utf-8") as f:
contenido = f.read()
2. Usar "w" cuando querías "a"
# ❌ Borra el log anterior en cada ejecución
with open("app.log", "w") as f:
f.write("Nueva entrada\n")
# ✅ Añade sin borrar
with open("app.log", "a") as f:
f.write("Nueva entrada\n")
3. Olvidar newline="" en CSV en Windows
# ❌ Genera líneas en blanco entre filas en Windows
with open("datos.csv", "w") as f:
writer = csv.writer(f)
# ✅
with open("datos.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
4. Confundir json.load con json.loads
cadena = '{"clave": "valor"}'
# ❌
datos = json.load(cadena) # TypeError: espera un archivo, no string
# ✅
datos = json.loads(cadena) # "s" = string
with open("f.json") as f:
datos = json.load(f) # sin "s" = objeto archivo
✅ Resumen y próximos pasos
Usa siempre with open() y especifica encoding="utf-8". El modo "r" lee, "w" crea o sobreescribe, "a" añade, "x" crea solo si no existe. Para datos tabulares, csv.DictReader y DictWriter. Para configuraciones y APIs, json.load y json.dump. Y pathlib.Path para trabajar con rutas de forma portable y expresiva.
La siguiente lección: módulos y paquetes — cómo organizar el código en varios archivos, importar módulos estándar y trabajar con pip.
❓ Preguntas frecuentes
❓ Preguntas frecuentes sobre Archivos en Python: leer y escribir texto, CSV y JSON
Las dudas más comunes respondidas de forma clara y directa.
💬 Foro de discusión
¿Tienes dudas sobre Archivos en Python: leer y escribir texto, CSV y JSON? Comparte tu pregunta con la comunidad.
Todavía no hay mensajes. ¡Sé el primero en participar!