Casi todo programa acaba leyendo o escribiendo algo. Esta lección va de pathlib, que sustituyó a os.path hace diez años y aún no todos se han enterado, y de los dos formatos que te vas a encontrar siempre: JSON y CSV.
pathlib: las rutas son objetos
from pathlib import Path
base = Path("datos")
f = base / "informe.txt" # el / une rutas
f.name # informe.txt
f.stem # informe
f.suffix # .txt
f.parent # datos
f.exists()
salidaruta datos/informe.txt name informe.txt stem informe suffix .txt parent datos ¿existe? False
Ese / entre un Path y un texto no es una división: es el operador de unión de rutas, y funciona igual en Windows. Sustituye a os.path.join, y con él a casi todo os.path.
os.path.join, os.listdir o rutas como cadenas en un tutorial, es de antes de 2016. pathlib es lo que se usa hoy: menos código, menos errores con las barras y métodos que se leen.Leer y escribir
base.mkdir(parents=True, exist_ok=True)
f.write_text("primera línea\nsegunda línea\n", encoding="utf-8")
f.read_text(encoding="utf-8")
f.read_text(encoding="utf-8").splitlines()
f.stat().st_size
salidaescrito, 30 bytes leído: 'primera línea\nsegunda línea\n' líneas: ['primera línea', 'segunda línea']
parents=True crea las carpetas intermedias y exist_ok=True evita el error si ya existe; juntos equivalen a mkdir -p.
encoding="utf-8". Sin él, Python usa la codificación por omisión del sistema, que en Windows no es UTF-8: el mismo programa funciona en tu portátil y falla en otra máquina con un UnicodeDecodeError. Es uno de los fallos más molestos de diagnosticar, y se evita escribiendo nueve caracteres.Sin cargarlo todo en memoria
with f.open(encoding="utf-8") as fh:
for i, linea in enumerate(fh, 1):
print(i, linea.rstrip())
salida1: primera línea 2: segunda línea
Un archivo abierto es iterable: el for va línea a línea sin leerlo entero. Con un registro de varios gigas, read_text() no es opción y esto sí.
Recorrer carpetas
base.iterdir() # lo que hay dentro, un nivel
base.glob("*.txt") # por patrón
base.rglob("*.txt") # y en todas las subcarpetas
salidaiterdir: ['informe.txt', 'nota.md', 'sub'] glob *.txt: ['informe.txt'] rglob **: ['informe.txt', 'sub/otro.txt']
Los tres devuelven generadores, no listas: para ordenarlos hace falta sorted(...) alrededor, como en la salida de arriba.
JSON
import json texto = json.dumps(datos, ensure_ascii=False, indent=2) vuelta = json.loads(texto)
salida{
"nombre": "Ana",
"edad": 30,
"lenguajes": [
"python",
"go"
],
"activo": true,
"saldo": null
}
ida y vuelta igual: True
Dos argumentos que casi siempre quieres: ensure_ascii=False, para que los acentos salgan como acentos y no como \u00e1; e indent=2 si el archivo lo va a leer una persona.
| Python | JSON |
|---|---|
dict |
objeto |
list, tuple |
array (las tuplas vuelven como listas) |
str |
string |
int, float |
number |
True / False |
true / false |
None |
null |
Y lo que no sabe convertir:
salidaTypeError: Object of type date is not JSON serializable
Fechas, Decimal, conjuntos y tus propias clases hay que convertirlos a mano, con el argumento default= de dumps o pasando primero por dataclasses.asdict.
CSV, que es otro módulo
import csv
w = csv.writer(f, delimiter=";")
w.writerow(["nombre", "ciudad", "precio"])
for fila in csv.DictReader(f, delimiter=";"):
print(fila["nombre"])
salidaescrito: 'nombre;ciudad;precio\r\n"bufanda ""larga""";A Coruña;9.00\r\n'
leído: {'nombre': 'bufanda "larga"', 'ciudad': 'A Coruña', 'precio': '9.00'}
Fíjate en que el módulo entrecomilla solo el campo que llevaba comillas dentro, y al leerlo lo devuelve entero. Por eso se usa csv y no linea.split(";"), que se rompe con el primer campo que contenga el separador.
DictReader usa la primera fila como nombres de columna y devuelve diccionarios, que se leen mucho mejor que fila[2]. Y al abrir el archivo para escribir CSV, hazlo con newline="", o en Windows saldrán líneas en blanco.
| Para… | Usa |
|---|---|
| Construir una ruta | Path("a") / "b" |
| Crear carpetas | p.mkdir(parents=True, exist_ok=True) |
| Leer un archivo pequeño | p.read_text(encoding="utf-8") |
| Leer uno grande | for linea in p.open(...) |
| Buscar archivos | p.rglob("*.txt") |
| JSON con acentos | json.dumps(x, ensure_ascii=False) |
| CSV | el módulo csv, nunca split |
Todo el código de esta lección se ejecutó con Python 3.14 en un contenedor limpio antes de publicarla; las salidas y los errores están copiados de esa ejecución.