Lectura y escritura de archivos con File e IO
En Ruby, la entrada/salida (I/O) te permite leer y escribir datos fuera de tu programa: archivos de texto, reportes, exportaciones y datos para otros scripts. La clase File es la puerta de entrada más común para trabajar con archivos en disco, y se apoya en la abstracción de IO (un “flujo” del que puedes leer o al que puedes escribir).
Apertura de archivos: modos más usados
Al abrir un archivo, debes elegir un modo. Los más comunes:
"r": lectura (falla si no existe)."w": escritura (crea o sobrescribe)."a": append (crea o agrega al final)."r+": lectura y escritura (no trunca)."w+": lectura y escritura (trunca)."a+": lectura y escritura (agrega al final)."rb","wb": modos binarios (útiles en Windows o para archivos no-texto).
En la práctica, para texto suele bastar con "r", "w" o "a".
Uso de bloques: cierre seguro del archivo
La forma recomendada es abrir con bloque: Ruby cierra el archivo automáticamente al terminar el bloque, incluso si ocurre un problema dentro.
File.open("data/notas.txt", "w") do |f| f.puts "Primera línea" f.puts "Segunda línea"endSi abres sin bloque, debes cerrar manualmente (y es fácil olvidarlo):
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
f = File.open("data/notas.txt", "a")f.puts "Otra línea"f.closeRutas y manejo de paths de forma portable
Evita “armar rutas” concatenando strings con "/". En distintos sistemas operativos el separador puede variar. Usa File.join y utilidades de File/Dir para mantener tu código portable.
Construir rutas y asegurar carpetas
base = Dir.pwd # carpeta actual del proyectoinput_path = File.join(base, "data", "usuarios.csv")output_path = File.join(base, "out", "usuarios.json")Dir.mkdir(File.join(base, "out")) unless Dir.exist?(File.join(base, "out"))Operaciones comunes con rutas
- ¿Existe?:
File.exist?(path) - ¿Es archivo?:
File.file?(path) - ¿Es directorio?:
File.directory?(path) - Extensión:
File.extname(path) - Nombre base:
File.basename(path) - Directorio padre:
File.dirname(path)
Leer texto: todo de una vez vs. línea a línea
Para archivos pequeños, puedes leer todo el contenido de una vez. Para archivos medianos/grandes, es mejor procesar línea a línea para ahorrar memoria.
Leer todo el archivo
contenido = File.read("data/notas.txt")puts contenidoProcesar línea a línea
File.foreach("data/notas.txt") do |linea| puts "Línea: #{linea.strip}"endFile.foreach es muy práctico porque abre, itera y cierra internamente.
Escritura incremental (reportes)
Para generar reportes, suele convenir ir escribiendo conforme procesas datos:
File.open("out/reporte.txt", "w") do |f| f.puts "reporte generado" f.puts "-" * 20 (1..3).each do |i| f.puts "fila #{i}" endendPersistir datos simples
Cuando solo necesitas guardar algo sencillo (por ejemplo, un log o un listado), un archivo de texto suele ser suficiente. Algunas ideas:
- Guardar una lista: una línea por elemento.
- Guardar pares clave-valor:
clave=valorpor línea. - Guardar un “snapshot” de un reporte: encabezado + filas.
Ejemplo: guardar IDs procesados:
ids = ["u001", "u002", "u003"]File.open("out/ids_procesados.txt", "w") do |f| ids.each { |id| f.puts id }endFormatos comunes para intercambiar información: CSV y JSON
Dos formatos muy usados para mover datos entre scripts y herramientas son:
- CSV: ideal para tablas (filas/columnas), compatible con hojas de cálculo.
- JSON: ideal para estructuras (objetos, listas), muy común en APIs y configuración.
CSV en Ruby: leer y escribir con la librería estándar
Ruby incluye csv en su librería estándar. Para trabajar con encabezados, usa headers: true.
require "csv"CSV.foreach("data/usuarios.csv", headers: true) do |row| # row es un CSV::Row, puedes acceder por nombre de columna puts row["email"]endEscritura de CSV:
require "csv"filas = [ ["id", "email", "activo"], ["u001", "ana@example.com", true], ["u002", "bob@example.com", false]]CSV.open("out/usuarios_export.csv", "w") do |csv| filas.each { |fila| csv << fila }endJSON en Ruby: leer y escribir con la librería estándar
Ruby incluye json. Para convertir estructuras Ruby a JSON, usa JSON.generate o JSON.pretty_generate (más legible). Para leer, usa JSON.parse.
require "json"data = {"app" => "mi_script", "version" => 1, "items" => [1, 2, 3]}File.write("out/data.json", JSON.pretty_generate(data))require "json"raw = File.read("out/data.json")obj = JSON.parse(raw)puts obj["app"]Nota: al parsear JSON, las claves suelen quedar como strings (a menos que transformes claves explícitamente).
Actividad aplicada: importar CSV, limpiar/transformar y exportar JSON
Objetivo: tomar un CSV de “usuarios” o “productos”, normalizar campos (limpieza), transformar tipos y exportar un JSON con una estructura clara para que otro script lo consuma.
1) Preparar el CSV de entrada
Ejemplo de archivo data/usuarios.csv (con encabezados):
id,nombre,email,edad,activo,plan,último_loginu001, Ana Pérez , ANA@EXAMPLE.COM , 29 , si , pro , 2025-01-10u002,Bob, bob@example.com, , no, free, 2024-12-01u003,Carla, carla@example.com, 41, SI, pro,Observa problemas típicos: espacios extra, mayúsculas en email, edad vacía, booleanos como “si/no”, fechas vacías.
2) Definir funciones de limpieza y transformación
Centraliza reglas para que el procesamiento sea consistente.
def clean_str(value) return nil if value.nil? s = value.to_s.strip s.empty? ? nil : senddef clean_email(value) s = clean_str(value) s&.downcaseenddef to_int(value) s = clean_str(value) s ? s.to_i : nilenddef to_bool(value) s = clean_str(value)&.downcase return true if ["si", "sí", "true", "1", "yes", "y"].include?(s) return false if ["no", "false", "0", "n"].include?(s) nilend3) Leer el CSV con encabezados y construir una estructura Ruby
Generaremos un JSON con metadatos y una lista de usuarios normalizados.
require "csv"require "json"input_path = File.join(Dir.pwd, "data", "usuarios.csv")output_dir = File.join(Dir.pwd, "out")output_path = File.join(output_dir, "usuarios.json")Dir.mkdir(output_dir) unless Dir.exist?(output_dir)usuarios = []errores = []CSV.foreach(input_path, headers: true) do |row| id = clean_str(row["id"]) nombre = clean_str(row["nombre"]) email = clean_email(row["email"]) edad = to_int(row["edad"]) activo = to_bool(row["activo"]) plan = clean_str(row["plan"])&.downcase ultimo_login = clean_str(row["último_login"]) || clean_str(row["último_login"]) # Validaciones mínimas para el ejemplo if id.nil? || email.nil? errores << {"id" => id, "motivo" => "Falta id o email", "fila" => row.to_h} next end usuarios << { "id" => id, "nombre" => nombre, "email" => email, "edad" => edad, "activo" => activo, "plan" => plan || "free", "ultimo_login" => ultimo_login }endpayload = { "fuente" => File.basename(input_path), "total" => usuarios.size, "errores" => errores.size, "usuarios" => usuarios, "rechazados" => errores}File.write(output_path, JSON.pretty_generate(payload))puts "Exportado: #{output_path}"puts "Usuarios: #{usuarios.size} | Rechazados: #{errores.size}"4) Procesar línea a línea y generar un reporte adicional
Además del JSON, es común crear un reporte de auditoría (por ejemplo, cuántos usuarios por plan). Esto refuerza el flujo: leer → transformar → escribir.
conteo_por_plan = Hash.new(0)usuarios.each do |u| conteo_por_plan[u["plan"]] += 1endreport_path = File.join(output_dir, "reporte_planes.txt")File.open(report_path, "w") do |f| f.puts "reporte de planes" f.puts "archivo: #{File.basename(input_path)}" f.puts "-" * 30 conteo_por_plan.sort_by { |plan, n| [-n, plan] }.each do |plan, n| f.puts "#{plan}: #{n}" endend5) Verificar resultados rápidamente
Comprueba que el JSON se generó y que tiene la estructura esperada:
require "json"obj = JSON.parse(File.read(File.join(Dir.pwd, "out", "usuarios.json")))puts obj["total"]puts obj["usuarios"].firstTabla de referencia rápida
| Tarea | Herramienta | Ejemplo |
|---|---|---|
| Leer archivo completo | File.read | File.read(path) |
| Escribir archivo completo | File.write | File.write(path, str) |
| Iterar líneas | File.foreach | File.foreach(path) { |l| ... } |
| Abrir con cierre seguro | File.open + bloque | File.open(path, "w") { |f| ... } |
| Construir rutas | File.join | File.join(a, b, c) |
| Leer CSV | CSV.foreach | CSV.foreach(path, headers: true) |
| Escribir JSON | JSON.pretty_generate | File.write(path, JSON.pretty_generate(obj)) |