web-dev-qa-db-fra.com

Quelle est la manière de facto de lire et d’écrire des fichiers dans Rust 1.x?

Avec Rust étant relativement nouveau, j'ai trop de façons de lire et d'écrire des fichiers. Beaucoup sont des extraits extrêmement compliqués que quelqu'un a proposés pour leur blog et 99% des exemples que j'ai Nous avons trouvé (même sur Stack Overflow) des constructions instables qui ne fonctionnent plus. Maintenant que Rust est stable, qu’est-ce qu’un extrait de code simple, lisible et non paniqué pour la lecture ou l’écriture de fichiers?

C’est ce qui me rapproche le plus de la lecture d’un fichier texte, mais elle n’est toujours pas compilée, même si je suis à peu près certaine d’avoir inclus tout ce que je devrais avoir. Ceci est basé sur un extrait que j'ai trouvé sur Google+ de tous les endroits, et la seule chose que j'ai changée est que l'ancien BufferedReader est maintenant juste BufReader:

use std::fs::File;
use std::io::BufReader;
use std::path::Path;

fn main() {
    let path = Path::new("./textfile");
    let mut file = BufReader::new(File::open(&path));
    for line in file.lines() {
        println!("{}", line);
    }
}

Le compilateur se plaint:

error: the trait bound `std::result::Result<std::fs::File, std::io::Error>: std::io::Read` is not satisfied [--explain E0277]
 --> src/main.rs:7:20
  |>
7 |>     let mut file = BufReader::new(File::open(&path));
  |>                    ^^^^^^^^^^^^^^
note: required by `std::io::BufReader::new`

error: no method named `lines` found for type `std::io::BufReader<std::result::Result<std::fs::File, std::io::Error>>` in the current scope
 --> src/main.rs:8:22
  |>
8 |>     for line in file.lines() {
  |>                      ^^^^^

En résumé, ce que je recherche, c'est:

  • brièveté
  • lisibilité
  • couvre toutes les erreurs possibles
  • ne panique pas
99
Jared

Aucune des fonctions que je montre ici ne s'affole d'elles-mêmes, mais j'utilise expect car je ne sais pas quel type de gestion des erreurs s'intégrera le mieux dans votre application. Allez lire Le Rust Langage de programmation ' chapitre sur le traitement des erreurs pour comprendre comment gérer correctement les échecs dans votre propre programme.

Rouille 1.26 et plus

Si vous ne voulez pas vous soucier des détails sous-jacents, il existe des fonctions d'une ligne pour la lecture et l'écriture.

Lire un fichier sur un String

use std::fs;

fn main() {
    let data = fs::read_to_string("/etc/hosts").expect("Unable to read file");
    println!("{}", data);
}

Lire un fichier en tant que Vec<u8>

use std::fs;

fn main() {
    let data = fs::read("/etc/hosts").expect("Unable to read file");
    println!("{}", data.len());
}

Écrire un fichier

use std::fs;

fn main() {
    let data = "Some data!";
    fs::write("/tmp/foo", data).expect("Unable to write file");
}

Rouille 1.0 et plus

Ces formulaires sont légèrement plus explicites que les fonctions sur une ligne qui allouent un String ou Vec, mais sont plus puissants dans la mesure où vous pouvez réutiliser les données allouées ou ajouter un objet existant.

Lecture de données

La lecture d'un fichier nécessite deux éléments essentiels: File et Read .

Lire un fichier sur un String

use std::fs::File;
use std::io::Read;

fn main() {
    let mut data = String::new();
    let mut f = File::open("/etc/hosts").expect("Unable to open file");
    f.read_to_string(&mut data).expect("Unable to read string");
    println!("{}", data);
}

Lire un fichier en tant que Vec<u8>

use std::fs::File;
use std::io::Read;

fn main() {
    let mut data = Vec::new();
    let mut f = File::open("/etc/hosts").expect("Unable to open file");
    f.read_to_end(&mut data).expect("Unable to read data");
    println!("{}", data.len());
}

Écrire un fichier

L'écriture d'un fichier est similaire, sauf que nous utilisons le trait Write et que nous écrivons toujours des octets. Vous pouvez convertir un String/&str octets avec as_bytes :

use std::fs::File;
use std::io::Write;

fn main() {
    let data = "Some data!";
    let mut f = File::create("/tmp/foo").expect("Unable to create file");
    f.write_all(data.as_bytes()).expect("Unable to write data");
}

I/O en mémoire tampon

Je me sentais un peu poussé par la communauté à utiliser BufReader et BufWriter au lieu de lire directement à partir d'un fichier.

Un lecteur (ou un écrivain) mis en mémoire tampon utilise un tampon pour réduire le nombre de demandes d'E/S. Par exemple, il est beaucoup plus efficace d’accéder au disque une fois pour lire 256 octets au lieu d’y accéder 256 fois.

Cela étant dit, je ne crois pas qu'un lecteur/écrivain en mémoire tampon sera utile lors de la lecture de l'ensemble du fichier. read_to_end semble copier les données sous forme de gros morceaux, le transfert peut donc déjà être naturellement fusionné en moins de demandes d'E/S.

Voici un exemple d'utilisation pour la lecture:

use std::fs::File;
use std::io::{BufReader, Read};

fn main() {
    let mut data = String::new();
    let f = File::open("/etc/hosts").expect("Unable to open file");
    let mut br = BufReader::new(f);
    br.read_to_string(&mut data).expect("Unable to read string");
    println!("{}", data);
}

Et pour écrire:

use std::fs::File;
use std::io::{BufWriter, Write};

fn main() {
    let data = "Some data!";
    let f = File::create("/tmp/foo").expect("Unable to create file");
    let mut f = BufWriter::new(f);
    f.write_all(data.as_bytes()).expect("Unable to write data");
}

Un BufReader est plus utile lorsque vous voulez lire ligne par ligne:

use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() {
    let f = File::open("/etc/hosts").expect("Unable to open file");
    let f = BufReader::new(f);

    for line in f.lines() {
        let line = line.expect("Unable to read line");
        println!("Line: {}", line);
    }
}
140
Shepmaster