Lectura, modificación y escritura de archivos
Modos de apertura: lectura, escritura, añadido
open(ruta, modo) acepta un segundo argumento que dice qué vas a hacer con el archivo — el mismo concepto que > y >> de la terminal (módulo 5), ahora dentro de Python:
| Modo | Significa | Equivalente en shell |
|---|---|---|
"r" (por defecto) |
leer | — |
"w" |
escribir, sobrescribiendo si ya existe | > |
"a" |
añadir al final, sin borrar lo que había | >> |
with open("species_observations.csv") as f:
contenido = f.read() # todo el archivo como una sola cadena
print(contenido[:50]) # primeros 50 caracteresf.read() trae el archivo entero de una vez, como cadena; f.readlines() (módulo 7) lo trae como una lista de líneas; iterar con for linea in f: los lee uno a uno sin cargar el archivo entero en memoria — la opción más segura para archivos grandes.
Escribir archivos con .write()
with open("species_observations.csv") as f:
lineas = f.readlines()
with open("resumen.txt", "w") as f:
for linea in lineas[1:]: # nos saltamos la cabecera
campos = linea.strip().split(",")
sample_id, especie, count = campos[0], campos[5], campos[6]
f.write(f"{sample_id}: {especie} ({count} individuos)\n").write(), a diferencia de print(), no añade un salto de línea automáticamente — por eso el \n al final de la f-string es necesario; sin él, todas las líneas quedarían pegadas en una sola.
Para añadir algo más sin borrar lo ya escrito, abre en modo "a":
with open("resumen.txt", "a") as f:
f.write(f"Total de observaciones: {len(lineas) - 1}\n")Un problema real: por qué split(",") no basta
Ya usas .split(",") desde el módulo 8 para trocear una línea CSV. Pruébalo sobre muestras_con_comas.csv:
with open("muestras_con_comas.csv") as f:
for linea in f:
campos = linea.strip().split(",")
print(len(campos), campos)3 ['sample_id', 'especie', 'notas']
4 ['M01', 'Quercus robur', '"Ejemplar sano', ' sin danos visibles"']
4 ['M02', 'Pinus sylvestris', '"Corteza danada', ' posible plaga"']
3 ['M03', 'Fragaria vesca', '"Sin observaciones"']
Las filas M01 y M02 dan 4 campos en vez de 3: split(",") no sabe que la coma de "Ejemplar sano, sin danos visibles" está protegida por las comillas, y la interpreta como un separador más. El resultado es una lista con el campo de notas partido por la mitad — un error silencioso que desplazaría todos los datos si intentaras acceder a campos[2] esperando la nota completa.
La solución es no reinventar el analizador de CSV: Python trae uno en la librería estándar, el módulo csv, que sí entiende las comillas:
import csv
with open("muestras_con_comas.csv") as f:
lector = csv.reader(f)
for fila in lector:
print(len(fila), fila)3 ['sample_id', 'especie', 'notas']
3 ['M01', 'Quercus robur', 'Ejemplar sano, sin danos visibles']
3 ['M02', 'Pinus sylvestris', 'Corteza danada, posible plaga']
3 ['M03', 'Fragaria vesca', 'Sin observaciones']
Ahora sí: siempre 3 campos, con la coma de la nota intacta dentro del campo, sin comillas de sobra.
Leer y escribir filas como diccionarios, a mano
csv.reader te da cada fila como una lista (accedes por posición, como con .split(",")). Para trabajar con cada fila como un diccionario — conectando con lo que aprendiste en el módulo 8 — no hace falta ninguna herramienta nueva: basta con trocear la cabecera igual que cualquier otra línea, y emparejar cada nombre de columna con su valor:
with open("species_observations.csv") as f:
lineas = f.readlines()
cabecera = lineas[0].strip().split(",")
for linea in lineas[1:]:
valores = linea.strip().split(",")
fila = dict(zip(cabecera, valores))
print(fila["species"])zip(cabecera, valores) empareja cada nombre de columna ("species", "count"…) con el valor de esa misma posición en la fila; dict(...) convierte esos pares en un diccionario. Ten en cuenta que, al construirlo con .split(",") en vez de csv.reader, vuelves a estar expuesto al problema de las comas dentro de las notas que viste más arriba — aquí funciona porque species_observations.csv no tiene comas protegidas por comillas.
Para escribir diccionarios de vuelta a un CSV, el proceso es el inverso: unes los valores con ",".join(...), en el mismo orden que la cabecera.
with open("species_observations.csv") as f:
lineas = f.readlines()
cabecera = lineas[0].strip().split(",")
abundantes = []
for linea in lineas[1:]:
valores = linea.strip().split(",")
fila = dict(zip(cabecera, valores))
if int(fila["count"]) > 3:
abundantes.append(fila)
with open("solo_abundantes.csv", "w") as f:
f.write(",".join(cabecera) + "\n")
for fila in abundantes:
f.write(",".join(fila[campo] for campo in cabecera) + "\n")int(fila["count"]) sigue siendo imprescindible: como viste en el módulo 8, todo lo que sale de un CSV es texto, y "12" > 3 daría un error en Python (no se puede comparar texto con número). Al escribir, fila[campo] for campo in cabecera recorre las columnas en el mismo orden que la cabecera, así que cada valor cae en su columna correcta aunque el diccionario en sí no garantice ningún orden.
Ejercicios
- Abre
species_observations.csven modo lectura y usa.read()para imprimir todo su contenido de una vez. - Usa
.readlines()y un bucleforpara imprimir solo elsample_id(primera columna) de cada fila, saltándote la cabecera. - Escribe un archivo nuevo
resumen.txtcon una línea por observación, con el formato"<sample_id>: <especie> (<count> individuos)". - Añade (modo
"a") una línea final aresumen.txtcon el total de observaciones. - Sobre
muestras_con_comas.csv, trocea cada línea con.split(",")e imprime cuántos campos obtienes por fila. Comprueba que las filas con coma dentro de las notas dan 4 campos en vez de 3. - Repite el ejercicio anterior con
csv.readery comprueba que ahora todas las filas dan exactamente 3 campos. - Sin usar el módulo
csv, abrespecies_observations.csv, trocea la cabecera y cada fila con.split(","), construye cada fila como diccionario condict(zip(cabecera, valores))e imprimefila["species"]. - (Reto) A partir del ejercicio anterior, construye una lista
abundantescon las filas dondecountsea mayor que 3, y escribesolo_abundantes.csva mano, uniendo cada fila con",".join(...)en el mismo orden que la cabecera.
Soluciones
with open("species_observations.csv") as f: print(f.read())with open("species_observations.csv") as f: lineas = f.readlines() for linea in lineas[1:]: print(linea.strip().split(",")[0])- Ver la sección “Escribir archivos con
.write()”. - Ver la sección “Escribir archivos con
.write()” (bloque con modo"a"). with open("muestras_con_comas.csv") as f: for linea in f: campos = linea.strip().split(",") print(len(campos), campos)import csv with open("muestras_con_comas.csv") as f: lector = csv.reader(f) for fila in lector: print(len(fila), fila)with open("species_observations.csv") as f: lineas = f.readlines() cabecera = lineas[0].strip().split(",") for linea in lineas[1:]: valores = linea.strip().split(",") fila = dict(zip(cabecera, valores)) print(fila["species"])with open("species_observations.csv") as f: lineas = f.readlines() cabecera = lineas[0].strip().split(",") abundantes = [] for linea in lineas[1:]: valores = linea.strip().split(",") fila = dict(zip(cabecera, valores)) if int(fila["count"]) > 3: abundantes.append(fila) with open("solo_abundantes.csv", "w") as f: f.write(",".join(cabecera) + "\n") for fila in abundantes: f.write(",".join(fila[campo] for campo in cabecera) + "\n")