2016-02-13 12 views
10

Esiste un modo idiomatico di elaborare un file un carattere alla volta in Rust?Leggi il file carattere per carattere in Rust

questo sembra essere più o meno quello che sto cercando:

let mut f = io::BufReader::new(try!(fs::File::open("input.txt"))); 

for c in f.chars() { 
    println!("Character: {}", c.unwrap()); 
} 

Ma Read::chars è ancora instabile come di Rust v1.6.0.

Ho considerato l'utilizzo di Read::read_to_string, ma il file potrebbe essere di grandi dimensioni e non voglio leggerlo tutto in memoria.

+1

Per alcuni tipi di file di testo:. 'F.lines() flat_map (| l | l.chars()) '... ma questa non è davvero una buona soluzione. –

+1

Hai appena considerato di copiare l'implementazione nel frattempo? Sono solo ~ 100 linee e significa che il tuo codice sarà banale da aggiornare se 'chars' si stabilizza così com'è. – Veedrac

risposta

3

Confrontiamo 4 approcci.

1. Read::chars

Si potrebbe copiare Read::chars realizzazione, ma è contrassegnato instabile con

la semantica di un parziale di lettura/scrittura di cui gli errori accadono Attualmente non è chiaro e può cambiare

quindi è necessario prestare attenzione. Ad ogni modo, questo sembra essere l'approccio migliore.

2. flat_map

Il flat_map alternativa non può essere compilato:

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

pub fn main() { 
    let mut f = BufReader::new(File::open("input.txt").expect("open failed")); 

    for c in f.lines().flat_map(|l| l.expect("lines failed").chars()) { 
     println!("Character: {}", c); 
    } 
} 

problemi è che chars prende in prestito dalla stringa, ma solo l.expect("lines failed") vita all'interno della chiusura, in modo compilatore dà l'errore borrowed value does not live long enough.

3. nidificati per

Questo codice

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

pub fn main() { 
    let mut f = BufReader::new(File::open("input.txt").expect("open failed")); 

    for line in f.lines() { 
     for c in line.expect("lines failed").chars() { 
      println!("Character: {}", c); 
     } 
    } 
} 

opere, ma mantiene assegnazione una stringa per ogni linea. Inoltre, se non ci sono interruzioni di riga sul file di input, l'intero file verrebbe caricato nella memoria.

4.BufRead::read_until

Una memoria efficiente alternativa per avvicinarsi 3 è quello di utilizzare Read::read_until, e utilizzare una singola stringa di leggere ogni riga:

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

pub fn main() { 
    let mut f = BufReader::new(File::open("input.txt").expect("open failed")); 

    let mut buf = Vec::<u8>::new(); 
    while f.read_until(b'\n', &mut buf).expect("read_until failed") != 0 { 
     // this moves the ownership of the read data to s 
     // there is no allocation 
     let s = String::from_utf8(buf).expect("from_utf8 failed"); 
     for c in s.chars() { 
      println!("Character: {}", c); 
     } 
     // this returns the ownership of the read data to buf 
     // there is no allocation 
     buf = s.into_bytes(); 
     buf.clear(); 
    } 
} 
0

Ci sono due soluzioni che hanno senso qui.

Innanzitutto, è possibile copiare l'implementazione di Read::chars() e utilizzarlo; ciò renderebbe completamente banale spostare il codice sull'implementazione della libreria standard se/quando si stabilizza.

D'altra parte, è possibile semplicemente ripetere riga per riga (utilizzando f.lines()) e quindi utilizzare line.chars() su ogni riga per ottenere i caratteri. Questo è un po 'più hacky, ma funzionerà sicuramente.

Se si desiderava solo un ciclo, è possibile utilizzare flat_map() con un lambda come |line| line.chars().

+0

Come testata, in genere è OK spostare i commenti utili alle risposte, ma se non si sta davvero aggiungendo al contenuto, è probabilmente [più accettabile contrassegnare la risposta come wiki della comunità] (http: //meta.stackoverflow com/q/269,913 mila/155 mila quattrocentoventitre). – Shepmaster

+0

Siamo spiacenti! Mi assicurerò di farlo la prossima volta. –

Problemi correlati