2012-11-27 8 views
31

Nota del redattore: questa domanda fa riferimento a parti di Rust precedenti a Rust 1.0, ma il concetto generale è ancora valido in Rust 1.0.Come leggere l'input dell'utente in Rust?

Ho intenzione di fare un tokenizzatore. Ho bisogno di leggere ogni riga dei tipi di utente e interrompere la lettura una volta che l'utente preme ctrl - D.

Ho cercato in giro e ho trovato solo one example su Rust IO che non viene nemmeno compilato. Ho esaminato la documentazione del modulo io e ho rilevato che la funzione read_line() fa parte dell'interfaccia , ma restituisce invece .

Il codice che vorrei sarebbe sostanzialmente simile al seguente in C++

vector<string> readLines() { 
    vector<string> allLines; 
    string line; 

    while (cin >> line) { 
     allLines.push_back(line); 
    } 

    return allLines; 
} 

risposta

0

Nota del redattore: Questa risposta precede Rust 1.0. Si prega di vedere le altre risposte per le soluzioni moderne.

A Rust 0.4, utilizzare il ReaderUtil tratto per accedere alla funzione read_line. Si noti che è necessario lanciare esplicitamente il valore di un tipo di carattere, ad esempio, reader as io::ReaderUtil

fn main() { 
     let mut allLines = ~[]; 
     let reader = io::stdin(); 

     while !reader.eof() { 
       allLines.push((reader as io::ReaderUtil).read_line()); 
     } 

     for allLines.each |line| { 
       io::println(fmt!("%s", *line)); 
     } 
} 
+1

Non credo Capisco come fusione funziona a Rust . :-(Non sembra tutto ciò che è simile ai cast a cui sono abituato in C/C++. Che cosa significa quando lanci qualcosa su un tipo di tratto? Semplicemente istruisce i compilatori a lasciarti legare quei metodi dal tipo di tratto sulla cosa da cui stai trasmettendo? (in questo caso, è qualsiasi cosa ritorna da 'stdin()' ...) –

+0

Non ho molta familiarità con gli interni di Rust, ma immagino che il cast sia per il tipo di controllo perché il metodo effettivo per chiamare è deciso in fase di esecuzione –

+1

hmm ok, questo è il tipo di soluzione che volevo, quindi la tua risposta è accettata :-) Grazie! –

1

Uh ... Dopo molte prove ed errori, ho trovato una soluzione.

Mi piacerebbe ancora vedere una soluzione migliore, quindi non accetterò la mia soluzione.

Il codice sottostante stampa esattamente ciò che l'utente inserisce.

mod tokenizer { 

pub fn read() -> ~[int] { 
    let reader = io::stdin(); 
    let mut bytes: ~[int] = ~[]; 

    loop { 
     let byte: int = reader.read_byte(); 
     if byte < 0 { 
      return bytes; 
     } 
     bytes += [byte]; 
    } 
} 

} 

fn main() { 
    let bytes: ~[int] = tokenizer::read(); 
    for bytes.each |byte| { 
     io::print(#fmt("%c", *byte as char)); 
    } 
} 
0

Questo è quello che ho messo a punto (con qualche aiuto dalla gente cordiale nel canale IRC#rust su irc.mozilla .org):

use core::io::ReaderUtil; 

fn read_lines() -> ~[~str] { 
    let mut all_lines = ~[]; 

    for io::stdin().each_line |line| { 
     // each_line provides us with borrowed pointers, but we want to put 
     // them in our vector, so we need to *own* the lines 
     all_lines.push(line.to_owned()); 
    } 

    all_lines 
} 

fn main() { 
    let all_lines = read_lines(); 

    for all_lines.eachi |i, &line| { 
     io::println(fmt!("Line #%u: %s", i + 1, line)); 
    } 
} 

E prova che funziona :)

$ rustc readlines.rs 
$ echo -en 'this\nis\na\ntest' | ./readlines 
Line #1: this 
Line #2: is 
Line #3: a 
Line #4: test 
124

Rust 1.x (vedi documentation):

012.
use std::io; 
use std::io::prelude::*; 

fn main() { 
    let stdin = io::stdin(); 
    for line in stdin.lock().lines() { 
     println!("{}", line.unwrap()); 
    } 
} 

ruggine 0,10-0,12 (vedi documentation):

use std::io; 

fn main() { 
    for line in io::stdin().lines() { 
     print!("{}", line.unwrap()); 
    } 
} 

ruggine 0,9 (vedi 0.9 documentation):

use std::io; 
use std::io::buffered::BufferedReader; 

fn main() { 
    let mut reader = BufferedReader::new(io::stdin()); 
    for line in reader.lines() { 
     print(line); 
    } 
} 

ruggine 0,8:

use std::io; 

fn main() { 
    let lines = io::stdin().read_lines(); 
    for line in lines.iter() { 
     println(*line); 
    } 
} 

ruggine 0,7:

use std::io; 

fn main() { 
    let lines = io::stdin().read_lines(); 
    for lines.iter().advance |line| { 
     println(*line); 
    } 
} 
+4

Vedere [Il futuro degli iteratori in Rust su rug-dev] (https://mail.mozilla.org/pipermail/rust-dev/2013-June/004364.html) per una discussione su come il ciclo 'for' potrebbe diventare più semplice in futuro (in pratica sbarazzarsi di '.iter(). advance'). – robinst

+0

Questo può essere effettivamente 'per io :: stdin(). Each_line | line | {..} ', o usando [' extra :: fileinput'] (http://static.rust-lang.org/doc/extra/fileinput.html): 'per fileinput :: input | line | {..} ' – huon

+1

La versione ruggine 1.0.0 (in alto) non viene più compilata (anche quando std :: io viene modificato in std :: old_io). :/ – weberc2

10

A partire dal 17 aprile 2015 da mdcox su mozilla rust irc.

use std::io; 

fn main() { 
    let mut stdin = io::stdin(); 
    let input = &mut String::new(); 

    loop { 
     input.clear(); 
     stdin.read_line(input); 
     println!("{}", input); 
    } 
} 
+2

Quando faccio 'stdin.read_line (input);', il compilatore mi avvisa che questa linea non viene usata. – yagooar

+1

@yagooar, dovrebbe avvisare del * risultato * non utilizzato, non della linea. Ciò significa che la lettura da stdin potrebbe aver provocato un errore ma non lo stai verificando. – gsingh2011

2

La domanda è leggere le righe da stdin e restituire un vettore. Su Rust 1.7:

fn readlines() -> Vec<String> { 
    use std::io::prelude::*; 
    let stdin = std::io::stdin(); 
    let v = stdin.lock().lines().map(|x| x.unwrap()).collect(); 
    v 
} 
0

Ci sono alcuni modi in cui riesco a pensare.

leggere tutte le input in un'unica String

let mut input = String::new(); 
io::stdin().read_to_end(&mut input); 

leggere le linee in Vector. Questo non è panic quando la lettura di una linea fallisce, invece salta quella linea fallita.

let stdin = io::stdin(); 
let locked = stdin.lock(); 
let v: Vec<String> = locked.lines().filter_map(|line| line.ok()).collect(); 

Inoltre se si vuole analizzarlo:

Dopo aver letto nella stringa di fare questo. È possibile analizzarlo in altre raccolte che implementano FromIterator. Gli elementi contenuti nella collezione devono anche implementare FromStr. Fino a quando il vincolo trait soddisfa è possibile modificare Vec a qualsiasi Collection:FromIterator, Collection<T: FromStr>

let v: Vec<i32> = "4 -42 232".split_whitespace().filter_map(|w| w.parse().ok()).collect(); 

Inoltre è possibile utilizzare sul StdinLock

let vv: Vec<Vec<i32>> = locked 
    .lines() 
    .filter_map(|l| 
     l.ok().map(|s| 
      s.split_whitespace().filter_map(|word| word.parse().ok()).collect() 
     ) 
    ) 
    .collect();