Lire et écrire des fichiers

RustBeginner
Pratiquer maintenant

Introduction

De nombreux programmes utiles franchissent les limites du système de fichiers : ils lisent des données existantes, les traitent, puis enregistrent un résultat. Ces opérations peuvent échouer. C’est pourquoi les API de fichiers de Rust renvoient des valeurs Result, au lieu de supposer que la réussite est garantie.

Vous allez examiner un journal de service préparé, représenter les emplacements de fichiers avec Path, lire du texte avec std::fs, créer un petit résumé, l’écrire dans un autre fichier et propager les erreurs d’E/S avec ?.

Lire un fichier texte avec un chemin

Dans cette étape, vous allez examiner un journal préparé et le charger dans une String Rust.

Le projet se trouve dans /home/labex/project/log-summary. Il contient le fichier d’entrée data/events.log, un répertoire output vide destiné au rapport final et le fichier source du programme src/main.rs. Accédez au projet :

cd /home/labex/project/log-summary

Ouvrez le fichier d’entrée avec nano pour savoir ce que le programme va lire :

nano data/events.log

Le fichier contient trois courts enregistrements. Ne le modifiez pas ; appuyez sur Ctrl+X pour quitter. Ouvrez ensuite le code source Rust :

nano src/main.rs

Les deux lignes use déjà présentes rendent les fonctions de fichier de std::fs et le type Path accessibles sous des noms abrégés. Il s’agit d’éléments d’importation, et non du sujet principal de ce laboratoire ; un module ultérieur explique en détail les chemins et les déclarations use. Le type de retour std::io::Result<()> indique que main se termine soit avec la valeur de réussite vide (), soit avec une erreur d’E/S. Le Ok(()) final représente le résultat réussi.

Remplacez le commentaire de l’étape 1 par :

    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 crée une vue empruntée du chemin du système de fichiers sans rien ouvrir. Le chemin relatif est résolu à partir du répertoire du projet depuis lequel vous exécutez Cargo. fs::read_to_string ouvre le fichier, lit le texte UTF-8 dans une String possédée et renvoie un Result. En cas de réussite, ? fournit la String ; en cas d’erreur d’E/S, il la renvoie depuis main. display() formate un chemin pour qu’il soit lisible, tandis que trim_end() empêche le saut de ligne final du fichier de créer une ligne vide supplémentaire dans la sortie.

Enregistrez avec Ctrl+O, appuyez sur Entrée, puis quittez avec Ctrl+X. Vérifiez et exécutez le programme :

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

Le chemin affiché et les trois enregistrements prouvent que le programme a lu le fichier attendu.

Compter les enregistrements du texte chargé

Dans cette étape, vous allez compter les lignes logiques de la String déjà présente en mémoire.

La méthode lines() crée un itérateur sur les lignes du texte sans modifier la chaîne source. Le consommateur count() renvoie le nombre d’éléments produits par l’itérateur. Ouvrez le code source :

nano src/main.rs

Remplacez le commentaire de l’étape 2 par :

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

Enregistrez et quittez, puis exécutez :

cargo run --quiet

La nouvelle dernière ligne doit être :

Line count: 3

Cela fonctionne que l’entrée se termine ou non par un saut de ligne, car lines() identifie les enregistrements logiques au lieu de compter les octets de saut de ligne.

Créer une chaîne de résumé stable

Dans cette étape, vous allez compter les enregistrements d’avertissement et formater les deux compteurs dans un texte prêt à être enregistré.

La méthode de chaîne starts_with teste un préfixe. En la combinant avec les adaptateurs d’itérateur que vous connaissez déjà, vous pouvez compter uniquement les lignes qui commencent par WARN. Ouvrez le code source :

nano src/main.rs

Remplacez le commentaire de l’étape 3 par :

    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());

Le prédicat de filtrage conserve les lignes d’avertissement, puis count() consomme cet itérateur filtré. format! renvoie une nouvelle String au lieu de l’afficher ; les caractères \n explicites créent deux lignes et ajoutent un saut de ligne final adapté à un fichier texte.

Enregistrez et quittez, puis vérifiez et exécutez :

cargo check
cargo run --quiet

La dernière section doit être :

Summary ready:
lines: 3
warnings: 1

Le résumé se trouve maintenant en mémoire, prêt à franchir la limite du système de fichiers.

Écrire et relire le fichier de résumé

Dans cette étape, vous allez écrire le résumé dans un répertoire de sortie préparé, puis le relire comme preuve de l’opération.

fs::write(path, data) crée un fichier ou remplace entièrement son ancien contenu par les octets fournis ; il n’ajoute pas de contenu à la fin. Comme la lecture, l’écriture peut échouer et renvoie un Result d’E/S. Vous allez donc propager l’erreur avec ?. Ouvrez le code source :

nano src/main.rs

Remplacez le commentaire de l’étape 4 par :

    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 emprunte le résumé pendant que write lit ses octets ; la String reste donc possédée par le programme. La relecture du fichier vérifie l’artefact réellement enregistré, au lieu de supposer que l’écriture a réussi.

Enregistrez et quittez, puis vérifiez et exécutez :

cargo check
cargo run --quiet

La dernière section doit être :

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

Pour une dernière vérification, ouvrez le fichier créé :

nano output/summary.txt

Vous devez voir les deux mêmes lignes de résumé. Appuyez sur Ctrl+X pour quitter sans effectuer de modification.

Résumé

Vous avez représenté les emplacements de fichiers avec Path, lu du texte UTF-8 avec fs::read_to_string, propagé les erreurs d’E/S avec ?, résumé les enregistrements chargés, écrit le résultat avec fs::write, puis relu le fichier enregistré pour confirmer son contenu.