Leer y escribir archivos

RustBeginner
Practicar Ahora

Introducción

Muchos programas útiles atraviesan los límites del sistema de archivos: leen datos existentes, los procesan y guardan un resultado. Estas operaciones pueden fallar, por lo que las API de archivos de Rust devuelven valores Result en lugar de suponer que el éxito está garantizado.

Inspeccionará un registro de servicio preparado, representará las ubicaciones de los archivos con Path, leerá texto mediante std::fs, creará un pequeño resumen, lo escribirá en otro archivo y propagará los errores de E/S con ?.

Leer un archivo de texto mediante un Path

En este paso, inspeccionará un registro preparado y lo leerá en un String de Rust.

El proyecto se encuentra en /home/labex/project/log-summary. Contiene data/events.log como entrada, un directorio output vacío para el informe posterior y src/main.rs como código fuente del programa. Entre en el proyecto:

cd /home/labex/project/log-summary

Abra el archivo de entrada con nano para saber qué leerá el programa:

nano data/events.log

El archivo contiene tres registros breves. No lo modifique; presione Ctrl+X para salir. Ahora abra el código fuente de Rust:

nano src/main.rs

Las dos líneas use preparadas permiten acceder mediante nombres abreviados a las funciones de archivos de std::fs y al tipo Path. Son declaraciones necesarias para los imports, pero no constituyen el tema principal de este laboratorio; el laboratorio posterior sobre módulos explica detalladamente las rutas y las declaraciones use. El tipo de retorno std::io::Result<()> indica que main puede completarse con el valor vacío () o devolver un error de E/S. El Ok(()) final representa el resultado correcto.

Reemplace el comentario del paso 1 por:

    let input_path = Path::new("data/events.log");
    let contents = fs::read_to_string(input_path)?;
    println!("Input: {}", input_path.display());
    println!("Contents:\n{}", contents.trim_end());

Path::new crea una vista prestada de la ruta del sistema de archivos sin abrir nada. La ruta relativa se resuelve desde el directorio del proyecto en el que ejecuta Cargo. fs::read_to_string abre el archivo, lee el texto UTF-8 en un String propio y devuelve un Result. En caso de éxito, ? produce el String; si ocurre un error de E/S, lo devuelve desde main. display() da formato a la ruta para mostrarla a las personas, y trim_end() evita que el salto de línea final del archivo genere una línea de salida en blanco adicional.

Guarde con Ctrl+O, presione Enter y salga con Ctrl+X. Compruebe y ejecute el programa:

cargo check
cargo run --quiet
Input: data/events.log
Contents:
INFO service started
WARN disk nearing limit
INFO backup complete

La ruta mostrada y los tres registros confirman que el programa leyó el archivo previsto.

Contar los registros del texto cargado

En este paso, contará las líneas lógicas del String que ya está en la memoria.

El método lines() crea un iterador sobre las líneas de texto sin modificar la cadena de origen. Su consumidor count() devuelve el número de elementos que produjo el iterador. Abra el código fuente:

nano src/main.rs

Reemplace el comentario del paso 2 por:

    let line_count = contents.lines().count();
    println!("Line count: {line_count}");

Guarde y salga; después, ejecute:

cargo run --quiet

La nueva última línea debe ser:

Line count: 3

Esto funciona tanto si la entrada termina con un salto de línea como si no, porque lines() identifica registros lógicos en lugar de contar bytes de salto de línea.

Crear una cadena de resumen estable

En este paso, contará los registros de advertencia y dará formato a ambos conteos en un texto listo para guardar.

El método de cadenas starts_with comprueba un prefijo. Al combinarlo con los adaptadores de iteradores que ya conoce, el programa puede contar únicamente las líneas que comienzan por WARN. Abra el código fuente:

nano src/main.rs

Reemplace el comentario del paso 3 por:

    let warning_count = contents
        .lines()
        .filter(|line| line.starts_with("WARN"))
        .count();
    let summary = format!("lines: {line_count}\nwarnings: {warning_count}\n");
    println!("Summary ready:\n{}", summary.trim_end());

El predicado del filtro conserva las líneas de advertencia, y count() consume ese iterador filtrado. format! devuelve un String nuevo en lugar de imprimirlo; los caracteres \n explícitos crean dos líneas y dejan un salto de línea final adecuado para un archivo de texto.

Guarde y salga; después, compruebe y ejecute:

cargo check
cargo run --quiet

La sección final debe ser:

Summary ready:
lines: 3
warnings: 1

El resumen ya está en la memoria del programa, listo para cruzar el límite del sistema de archivos.

Escribir y volver a leer el archivo de resumen

En este paso, escribirá el resumen en un directorio de salida preparado y lo leerá de nuevo como comprobación.

fs::write(path, data) crea un archivo o reemplaza por completo su contenido anterior con los bytes proporcionados; no añade contenido al final. Al igual que la lectura, la escritura puede fallar y devuelve un Result de E/S, por lo que propagará el error con ?. Abra el código fuente:

nano src/main.rs

Reemplace el comentario del paso 4 por:

    let output_path = Path::new("output/summary.txt");
    fs::write(output_path, &summary)?;
    let saved = fs::read_to_string(output_path)?;
    println!("Saved {}:\n{}", output_path.display(), saved.trim_end());

&summary toma prestado el resumen mientras write lee sus bytes, por lo que el String sigue siendo propiedad del programa. Volver a leer el archivo comprueba el artefacto guardado real en lugar de suponer que la escritura funcionó.

Guarde y salga; después, compruebe y ejecute:

cargo check
cargo run --quiet

La sección final debe ser:

Saved output/summary.txt:
lines: 3
warnings: 1

Para realizar una última comprobación, abra el archivo creado:

nano output/summary.txt

Debe ver las mismas dos líneas del resumen. Presione Ctrl+X para salir sin realizar cambios.

Resumen

Representó las ubicaciones de los archivos con Path, leyó texto UTF-8 mediante fs::read_to_string, propagó los errores de E/S con ?, resumió los registros cargados, escribió el resultado con fs::write y volvió a leer el archivo guardado para confirmar su contenido.