2015-07-02 4 views
38

Con Rust relativamente nuovo, ho visto troppi modi di leggere e scrivere file. Molti sono frammenti estremamente disordinati che qualcuno ha inventato per il loro blog e il 99% degli esempi che ho trovato (anche su Stack Overflow) provengono da build instabili che non funzionano più. Ora che Rust è stabile, cos'è uno snippet semplice, leggibile e non da prendere al panico per leggere o scrivere file?Qual è il modo de facto per leggere e scrivere file in Rust 1.x?

Questo è il modo più vicino a cui ho ottenuto qualcosa che funzioni in termini di lettura di un file di testo, ma non si sta ancora compilando anche se sono abbastanza certo di aver incluso tutto ciò che dovrei avere. Questo si basa fuori di un frammento che ho trovato su Google+ di tutti i luoghi, e l'unica cosa che ho cambiato è che il vecchio BufferedReader è ormai solo 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); 
    } 
} 

Il compilatore si lamenta:

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() { 
    |>      ^^^^^ 

per riassumere, quello che sto cercando è:

  • brevità
  • leggibilità
  • copre tutti i possibili errori
  • non fatevi prendere dal panico
+0

Come si desidera leggere il file? Lo vuoi line-by-line, come hai mostrato? Lo vuoi tutto in una stringa? C'è più di un modo per "leggere un file". – Shepmaster

+0

In entrambi i casi va bene. L'ho lasciato aperto intenzionalmente. Se viene raccolto tutto in una stringa, la suddivisione in un Vec sarebbe banale e viceversa. A questo punto della mia ricerca di soluzioni, sarò felice di vedere solo un codice I/O di file ruggine aggiornato e aggiornato che funziona. –

+1

Per quanto riguarda l'errore del tratto ('std :: io :: Read'), si noti che in Rust è necessario importare i tratti che si prevede utilizzino * esplicitamente *; quindi qui manca un 'use std :: io :: Read' (che potrebbe essere un' use std :: io :: {Read, BufReader} 'per unire i due usi insieme) –

risposta

49

Hai bisogno di due pezzi fondamentali: File e Read.

Se volete leggere tutto ad una 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); 
} 

Se volete leggere tutto come una serie di byte:

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

Nessuno del panico funzioni per conto proprio, ma Sto usando expect perché non so quale tipo di gestione degli errori si adatta meglio alla tua applicazione.

Queste sono leggermente più verbose delle versioni ipotetiche che allocano uno String o Vec per voi, ma sono più potenti in quanto è possibile riutilizzare i dati allocati o accodare a un oggetto esistente. Scrivere un piccolo wrapper che assegna l'oggetto per te è banale e potrebbe essere un'aggiunta ergonomica in futuro.

Scrivere un file è lo stesso, tranne che è sempre eseguito come byte. È possibile convertire un String/&str di byte con 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"); 
} 

mi sono sentito un po 'di una spinta da parte della comunità di usare BufReader e BufWriter invece di leggere direttamente da un file

A il lettore (o lo scrittore) bufferizzato utilizza un buffer per ridurre il numero di richieste IO. Ad esempio, è molto più efficiente accedere al disco una volta per leggere 256 byte invece di accedere al disco 256 volte.

Detto questo, non credo che un lettore/scrittore bufferizzato sarà utile quando si legge l'intero file.read_to_end sembra copiare i dati in blocchi piuttosto grandi, quindi il trasferimento potrebbe già essere naturalmente riunito in un numero inferiore di richieste di I/O.

Ecco un esempio di utilizzo:

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

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

E per la scrittura:

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

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 è più utile quando si desidera leggere riga per riga:

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

Nel Ru notturno st, ci sono funzioni di una sola riga per la lettura e la scrittura:

#![feature(fs_read_write)] 

use std::fs; 

fn main() { 
    let data = fs::read_string("/etc/hosts").expect("Unable to open file"); 
    println!("{}", data); 
} 
#![feature(fs_read_write)] 

use std::fs; 

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

use std::fs; 

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

Mi aspetto che questi diventino l'alternativa popolare una volta che sono stabilizzati.

+0

Non ho molto per basarmi su questo, ma durante la ricerca di questo ho sentito un po 'di spinta dalla comunità per utilizzare BufReader e BufWriter invece di leggere direttamente da un file a una stringa. Sai molto di questi oggetti o dei pro e contro del loro utilizzo rispetto alla versione "più classica" che hai mostrato nella tua risposta? –

+0

Che bel semplice esempio, sto già cercando ore per sth come questo. Grazie. – rebeling