← Volver al listado de tecnologías
Capítulo 4: Lector y Procesador CSV
Lector y Procesador CSV
Un procesador de archivos CSV que calcula estadísticas y transforma datos.
Conceptos que Aprenderás
- Parsing de texto estructurado
- Manejo de archivos grandes
- Streaming vs carga en memoria
- Generación de reportes
Python
from pathlib import Path
from dataclasses import dataclass
from typing import Iterator
import csv
@dataclass
class Estadisticas:
total: int = 0
suma: float = 0
minimo: float = float('inf')
maximo: float = float('-inf')
@property
def promedio(self) -> float:
return self.suma / self.total if self.total > 0 else 0
def leer_csv(archivo: Path) -> Iterator[dict]:
with open(archivo, newline='', encoding='utf-8') as f:
reader = csv.DictReader(f)
for fila in reader:
yield fila
def calcular_estadisticas(archivo: Path, columna: str) -> Estadisticas:
stats = Estadisticas()
for fila in leer_csv(archivo):
try:
valor = float(fila[columna])
stats.total += 1
stats.suma += valor
stats.minimo = min(stats.minimo, valor)
stats.maximo = max(stats.maximo, valor)
except (KeyError, ValueError):
continue
return stats
def filtrar_csv(archivo: Path, columna: str, valor: str) -> list[dict]:
return [fila for fila in leer_csv(archivo) if fila.get(columna) == valor]
def main():
archivo = Path(input("Archivo CSV: "))
if not archivo.exists():
print("Archivo no encontrado")
return
# Mostrar columnas disponibles
with open(archivo) as f:
columnas = next(csv.reader(f))
print(f"Columnas: {', '.join(columnas)}")
columna = input("Columna para estadísticas: ")
stats = calcular_estadisticas(archivo, columna)
print(f"\nEstadísticas de '{columna}':")
print(f" Total: {stats.total}")
print(f" Suma: {stats.suma:.2f}")
print(f" Promedio: {stats.promedio:.2f}")
print(f" Mínimo: {stats.minimo:.2f}")
print(f" Máximo: {stats.maximo:.2f}")
if __name__ == "__main__":
main()
Zig
const std = @import("std");
const Estadisticas = struct {
total: u64 = 0,
suma: f64 = 0,
minimo: f64 = std.math.inf(f64),
maximo: f64 = -std.math.inf(f64),
fn promedio(self: Estadisticas) f64 {
return if (self.total > 0) self.suma / @as(f64, @floatFromInt(self.total)) else 0;
}
};
fn parsearLinea(linea: []const u8, allocator: std.mem.Allocator) !std.ArrayList([]const u8) {
var campos = std.ArrayList([]const u8).init(allocator);
var iter = std.mem.splitScalar(u8, linea, ',');
while (iter.next()) |campo| {
try campos.append(campo);
}
return campos;
}
fn buscarIndice(columnas: []const []const u8, nombre: []const u8) ?usize {
for (columnas, 0..) |col, i| {
if (std.mem.eql(u8, std.mem.trim(u8, col, " \r"), nombre)) return i;
}
return null;
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const stdout = std.io.getStdOut().writer();
const stdin = std.io.getStdIn().reader();
var buf: [256]u8 = undefined;
try stdout.print("Archivo CSV: ", .{});
const ruta = stdin.readUntilDelimiter(&buf, '\n') catch return;
const archivo = std.fs.cwd().openFile(ruta, .{}) catch {
try stdout.print("Archivo no encontrado\n", .{});
return;
};
defer archivo.close();
var reader = archivo.reader();
var linea_buf: [4096]u8 = undefined;
// Leer encabezados
const encabezado = reader.readUntilDelimiter(&linea_buf, '\n') catch return;
var columnas = try parsearLinea(encabezado, allocator);
defer columnas.deinit();
try stdout.print("Columnas: ", .{});
for (columnas.items) |col| {
try stdout.print("{s}, ", .{col});
}
try stdout.print("\n", .{});
try stdout.print("Columna para estadísticas: ", .{});
const col_nombre = stdin.readUntilDelimiter(&buf, '\n') catch return;
const indice = buscarIndice(columnas.items, col_nombre) orelse {
try stdout.print("Columna no encontrada\n", .{});
return;
};
var stats = Estadisticas{};
while (reader.readUntilDelimiter(&linea_buf, '\n')) |linea| {
var campos = try parsearLinea(linea, allocator);
defer campos.deinit();
if (indice < campos.items.len) {
const valor = std.fmt.parseFloat(f64, campos.items[indice]) catch continue;
stats.total += 1;
stats.suma += valor;
stats.minimo = @min(stats.minimo, valor);
stats.maximo = @max(stats.maximo, valor);
}
} else |_| {}
try stdout.print("\nEstadísticas de '{s}':\n", .{col_nombre});
try stdout.print(" Total: {d}\n", .{stats.total});
try stdout.print(" Promedio: {d:.2}\n", .{stats.promedio()});
}
Diferencias Clave
| Aspecto | Python | Zig |
|---|---|---|
| CSV | Módulo csv | Parsing manual |
| Iteración | Generator yield | Reader línea por línea |
| Infinito | float(‘inf’) | std.math.inf |
Ejercicios
- Exporta resultados a nuevo CSV
- Soporta delimitadores personalizados
- Detecta y maneja valores vacíos