Operaciones con archivos en Python
Este artículo explica las operaciones con archivos en Python.
Con la "seguridad", "eficiencia" y "legibilidad" en mente, esta guía explica los conceptos paso a paso, desde los fundamentos hasta técnicas prácticas.
YouTube Video
Operaciones con archivos en Python
Las operaciones con archivos son una habilidad fundamental esencial, desde pequeños scripts hasta aplicaciones a gran escala.
Abrir y cerrar archivos
Primero, veamos ejemplos de cómo leer y escribir archivos de texto. Usar la instrucción with (administrador de contexto) garantiza que el archivo se cierre correctamente.
El siguiente código abre un archivo de texto, lee su contenido y lo procesa línea por línea.
1# Open and read a text file safely using a context manager.
2with open("example.txt", "r", encoding="utf-8") as f:
3 # Read all lines lazily (as an iterator) and process each line.
4 for line in f:
5 print(line.rstrip())- Especificar explícitamente
encoding="utf-8"ayuda a reducir problemas dependientes de la plataforma. - Incluso con archivos grandes,
for line in fes eficiente en el uso de memoria.
Escritura (sobrescribir y agregar)
Presta atención al modo al escribir en archivos. w es para sobrescribir, a es para agregar. Utiliza también la instrucción with al escribir.
El siguiente código muestra ejemplos básicos de sobrescritura y adición.
1# Write (overwrite) to a file.
2with open("output.txt", "w", encoding="utf-8") as f:
3 f.write("First line\n")
4 f.write("Second line\n")
5
6# Append to the same file.
7with open("output.txt", "a", encoding="utf-8") as f:
8 f.write("Appended line\n")- Este código escribe texto en
output.txty luego añade más texto al mismo archivo. El modo"w"sobrescribe el archivo, mientras que el modo"a"agrega una nueva línea al final del contenido existente. - Si necesitas vaciar el buffer, llama a
flush(), pero normalmente el contenido se vacía automáticamente al finalizar el contexto. - Si varios procesos o hilos pueden escribir al mismo tiempo, debes considerar el control exclusivo, como el bloqueo de archivos.
Lectura y escritura de datos binarios
Las imágenes y archivos comprimidos se trabajan en modo binario (rb o wb). A diferencia del modo texto, se ignora encoding.
El siguiente código lee un archivo binario y lo copia a otro archivo en modo binario.
1# Read and write binary files.
2with open("image.png", "rb") as src:
3 data = src.read()
4
5with open("copy.png", "wb") as dst:
6 dst.write(data)- Al manipular archivos binarios grandes, evita leer todo de una vez con
read(); leer y escribir en bloques es más eficiente en memoria.
Ejemplo de manejo de archivos grandes en bloques
Para archivos enormes que no caben en la memoria, léelos y escríbelos en bloques de tamaño fijo. Como depende de las operaciones de entrada/salida, ajustar el tamaño del búfer es útil.
El siguiente código copia un archivo en bloques de 64KB. Esto funciona rápidamente y mantiene bajo el uso de memoria.
1# Copy a large file in chunks to avoid using too much memory.
2CHUNK_SIZE = 64 * 1024 # 64 KB
3
4with open("large_source.bin", "rb") as src, open("large_dest.bin", "wb") as dst:
5 while True:
6 chunk = src.read(CHUNK_SIZE)
7 if not chunk:
8 break
9 dst.write(chunk)- Puedes ajustar el tamaño del bloque según las características de tu red o disco. En los SSD, un tamaño de bloque un poco mayor suele funcionar mejor.
Ejemplo usando el argumento buffering
Al especificar el argumento buffering en la función open(), puedes controlar el tamaño del búfer que utiliza internamente Python. Esto te permite optimizar aún más la eficiencia de entrada/salida.
1# Explicitly set the internal buffer size to 128 KB for faster I/O.
2CHUNK_SIZE = 64 * 1024
3BUFFER_SIZE = 128 * 1024 # 128 KB internal buffer
4
5with open("large_source.bin", "rb", buffering=BUFFER_SIZE) as src, open("large_dest.bin", "wb", buffering=BUFFER_SIZE) as dst:
6 while True:
7 chunk = src.read(CHUNK_SIZE)
8 if not chunk:
9 break
10 dst.write(chunk)- Si estableces el valor de
bufferingen 0, las operaciones de entrada/salida se realizan sin búfer; 1 habilita el búfer por líneas, mientras que valores de 2 o superiores usan un búfer del tamaño especificado en bytes. - En general, el valor predeterminado es suficiente porque el sistema operativo gestiona eficientemente la caché, pero ajustar este parámetro puede ser efectivo para archivos muy grandes o dispositivos especiales.
Operaciones modernas de archivos usando Pathlib
El módulo estándar pathlib hace que el manejo de rutas sea más intuitivo. Mejora la legibilidad y seguridad en comparación con rutas como cadenas de texto.
1from pathlib import Path
2
3path = Path("data") / "notes.txt"
4
5# Ensure parent directory exists.
6path.parent.mkdir(parents=True, exist_ok=True)
7
8# Write and read using pathlib.
9path.write_text("Example content\n", encoding="utf-8")
10content = path.read_text(encoding="utf-8")
11print(content)- Este código demuestra cómo crear un directorio con
pathlib, escribir en un archivo de texto y leer su contenido. Con un objetoPath, puedes manejar rutas de forma intuitiva y segura. Pathtiene APIs convenientes comoiterdir()yglob(). Puedes escribir código sin preocuparte por los separadores de rutas entre distintos sistemas operativos.
Archivos y directorios temporales (tempfile)
Los archivos temporales pueden crearse de forma segura con tempfile. Esto evita condiciones de competencia de seguridad y colisiones de nombres.
El siguiente código muestra un ejemplo de creación de datos temporales utilizando un archivo temporal.
1import tempfile
2
3# Create a temporary file that is deleted on close.
4with tempfile.NamedTemporaryFile(
5 mode="w+",
6 encoding="utf-8",
7 delete=True
8) as tmp:
9 tmp.write("temporary data\n")
10 tmp.seek(0)
11 print(tmp.read())
12
13# tmp is deleted here- Este código crea un archivo temporal, escribe y lee datos, y lo elimina automáticamente al finalizar el bloque
with. Usandotempfile.NamedTemporaryFile, puedes manejar archivos temporales de forma segura y sin conflictos. Como se especificadelete=True, el archivo se elimina automáticamente. - En Windows, puede que no puedas abrir el archivo desde otro identificador de inmediato; por eso, puedes establecer
delete=Falsey gestionar la eliminación tú mismo.
shutil: Operaciones de alto nivel para copiar, mover y eliminar
La copia, movimiento y eliminación recursiva de archivos y directorios es fácil con shutil.
1import shutil
2
3# Copy a file preserving metadata.
4shutil.copy2("source.txt", "dest.txt")
5
6# Move a file or directory.
7shutil.move("old_location", "new_location")
8
9# Remove an entire directory tree (use with caution).
10shutil.rmtree("some_directory")shutil.copy2también copia los metadatos (como la fecha de modificación).moverecurrirá a mover el archivo incluso si no se puede usar rename.rmtreees una operación peligrosa, así que siempre confirma y haz una copia de seguridad antes de borrar.
Metadatos de archivos (os.stat) y manejo de permisos
El tamaño de archivo, la fecha de modificación y los permisos pueden leerse y modificarse con os y stat.
El siguiente código obtiene información básica de archivos con os.stat y cambia los permisos con os.chmod.
1import os
2import stat
3from datetime import datetime
4
5st = os.stat("example.txt")
6print("size:", st.st_size)
7print("modified:", datetime.fromtimestamp(st.st_mtime))
8
9# Make file read-only for owner.
10os.chmod("example.txt", stat.S_IREAD)- Los permisos se comportan de manera diferente en sistemas POSIX y Windows. Si la compatibilidad multiplataforma es importante, usa APIs de alto nivel o agrega procesamiento condicional.
Bloqueo de archivos (control exclusivo): diferencias entre Unix y Windows
Se necesita control exclusivo cuando varios procesos acceden en paralelo al mismo archivo. UNIX usa fcntl y Windows usa msvcrt.
El siguiente código usa fcntl.flock en sistemas UNIX para adquirir un bloqueo exclusivo al escribir.
1# Unix-style file locking example
2import fcntl
3
4with open("output.txt", "a+", encoding="utf-8") as f:
5 fcntl.flock(f, fcntl.LOCK_EX)
6 try:
7 f.write("Safe write\n")
8 f.flush()
9 finally:
10 fcntl.flock(f, fcntl.LOCK_UN)- Este código adquiere un bloqueo exclusivo usando
fcntl.flocken sistemas tipo UNIX para escribir de forma segura y prevenir escrituras simultáneas en el archivo. Siempre libera el bloqueo después de procesar para permitir el acceso de otros procesos. - En Windows, usa
msvcrt.locking(). Para un uso de mayor nivel, considera bibliotecas externas comoportalockerofilelock.
Patrones atómicos de escritura de archivos
Para evitar la corrupción de archivos durante actualizaciones, escribe en un archivo temporal y reemplázalo usando os.replace al finalizar con éxito.
Escribir en un archivo temporal y luego reemplazarlo evita la corrupción si ocurre un fallo.
1import os
2from pathlib import Path
3import tempfile
4
5def atomic_write(path: Path, data: str, encoding="utf-8"):
6 # Write to a temp file in the same directory and atomically replace.
7 dirpath = path.parent
8 with tempfile.NamedTemporaryFile(
9 mode="w",
10 encoding=encoding,
11 dir=str(dirpath),
12 delete=False
13 ) as tmp:
14 tmp.write(data)
15 tmp.flush()
16 tempname = tmp.name
17
18 # Atomic replacement (on most OSes)
19 os.replace(tempname, str(path))
20
21# Usage
22atomic_write(Path("config.json"), '{"key":"value"}\n')os.replace realiza un reemplazo atómico dentro del mismo sistema de archivos. Ten en cuenta que la atomicidad no está garantizada entre distintos puntos de montaje.
Acceso rápido usando mmap (para datos a gran escala)
Para acceso aleatorio a archivos grandes, mmap mejora el rendimiento de E/S. Principalmente implica operaciones binarias.
El siguiente código asigna un archivo a memoria y lee o escribe rangos específicos de bytes. Ten cuidado al cambiar el tamaño del archivo.
1import mmap
2
3with open("data.bin", "r+b") as f:
4 mm = mmap.mmap(f.fileno(), 0) # map entire file
5 try:
6 print(mm[:20]) # first 20 bytes
7 mm[0:4] = b"\x00\x01\x02\x03" # modify bytes
8 finally:
9 mm.close()- Este código mapea un archivo binario a memoria con
mmapy realiza operaciones directas de lectura/escritura a nivel de bytes. La asignación de memoria permite el acceso aleatorio rápido a grandes conjuntos de datos. mmapes eficiente, pero un uso incorrecto puede causar problemas de consistencia de datos. Llama aflush()para sincronizar cuando sea necesario.
CSV / JSON / Pickle: lectura y escritura por formato
Los formatos de datos específicos tienen módulos dedicados. Usa csv para CSV, json para JSON y pickle para guardar objetos de Python.
El siguiente código proporciona ejemplos básicos de lectura y escritura de CSV y JSON, y el uso de Pickle. Como Pickle puede ejecutar código arbitrario, evita cargar datos de fuentes no confiables.
1import csv
2import json
3import pickle
4
5# CSV write
6with open("rows.csv", "w", encoding="utf-8", newline="") as f:
7 writer = csv.writer(f)
8 writer.writerow(["name", "age"])
9 writer.writerow(["Alice", 30])
10
11# JSON write
12data = {"items": [1, 2, 3]}
13with open("data.json", "w", encoding="utf-8") as f:
14 json.dump(data, f, ensure_ascii=False, indent=2)
15
16# Pickle write (only for trusted environments)
17obj = {"key": "value"}
18with open("obj.pkl", "wb") as f:
19 pickle.dump(obj, f)- Se recomienda especificar
newline=""para CSV para evitar líneas en blanco adicionales en Windows. Conensure_ascii=False, JSON mantiene los caracteres UTF-8 legibles.
Lectura y escritura directa de archivos comprimidos (gzip / bz2 / zipfile)
Manejar directamente gzip y zip puede ahorrar espacio en disco. La biblioteca estándar incluye módulos correspondientes.
El siguiente código es un ejemplo sencillo de cómo leer y escribir archivos gzip como texto.
1import gzip
2
3# Write gzipped text.
4with gzip.open("text.gz", "wt", encoding="utf-8") as f:
5 f.write("Compressed content\n")
6
7# Read gzipped text.
8with gzip.open("text.gz", "rt", encoding="utf-8") as f:
9 print(f.read())- Existe un compromiso entre la relación de compresión y la velocidad, dependiendo del nivel y formato de compresión.
Medidas de seguridad y vulnerabilidad
Se pueden considerar los siguientes puntos para las medidas de seguridad y vulnerabilidad.
- No utilices valores no confiables directamente en nombres o rutas de archivos.
- Usa Pickle solo con fuentes de confianza.
- Minimiza los privilegios de ejecución y otorga solo los permisos mínimos a los procesos que manejan archivos.
- Utiliza archivos temporales con
tempfiley no guardes archivos planos en directorios públicos.
Si usas la entrada del usuario en rutas de archivos, se requiere normalización y validación. Por ejemplo, usa Path.resolve() y verifica los directorios superiores.
1from pathlib import Path
2
3def safe_path(base_dir: Path, user_path: str) -> Path:
4 candidate = (base_dir / user_path).resolve()
5 if base_dir.resolve() not in candidate.parents and base_dir.resolve() != candidate:
6 raise ValueError("Invalid path")
7 return candidate
8
9# Usage
10base = Path("/srv/app/data")
11
12# Raises an exception: attempted path traversal outside `base`
13safe = safe_path(base, '../etc/passwd')- Ten especial cuidado al usar entradas externas como rutas de archivos en aplicaciones web o APIs públicas.
Resumen de patrones comunes
- Utiliza siempre la instrucción
with(cierre automático). - Especifica explícitamente el
encodingpara datos de texto. - Lee y escribe archivos grandes en bloques.
- Implementa el bloqueo de archivos para recursos compartidos.
- Para actualizaciones críticas, utiliza el patrón atómico de 'escribir en un archivo temporal → os.replace'.
- Confirma y crea copias de seguridad siempre antes de realizar operaciones peligrosas (como eliminar o sobrescribir).
- Normaliza y valida cuando uses entradas externas como rutas de archivo.
Resumen
Al realizar operaciones de archivos, es importante usar técnicas seguras y confiables, como utilizar el bloque with, especificar la codificación explícitamente y realizar escrituras atómicas. Para procesamiento a gran escala o acceso paralelo, es necesario implementar sistemas de bloqueo y gestión de registros para evitar la corrupción y los conflictos de datos. Equilibrar la eficiencia y la seguridad es la clave para operaciones de archivos fiables.
Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.