Note de l'éditeur: Cette question concerne des parties de Rust antérieures à Rust 1.0, mais le concept général est toujours valable dans Rust 1.0.
J'ai l'intention de faire un tokenizer. J'ai besoin de lire chaque ligne saisie par l'utilisateur et d'arrêter la lecture dès que l'utilisateur appuie ctrl-D.
J'ai cherché autour et trouvé seulement n exemple sur Rust IO qui ne compile même pas. J'ai consulté la documentation du module io
et découvert que la fonction read_line()
fait partie de l'interface ReaderUtil
, mais stdin()
renvoie plutôt un Reader
.
Le code que je voudrais ressembler essentiellement à ce qui suit en C++
vector<string> readLines () {
vector<string> allLines;
string line;
while (cin >> line) {
allLines.Push_back(line);
}
return allLines;
}
Uwaga edytora: Ta odpowiedź poprzedza Rust 1.0. Zobacz inne odpowiedzi na nowoczesne rozwiązania.
W Rust 0.4 u funyj funkcji ReaderUtil
, aby uzyskać dostęp do funkcji read_line
. Zauważ, e musisz jawnie przekazać wartość do typu cechy, np. 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));
}
}
Rust 1.x (voir documentation ):
use std::io;
use std::io::prelude::*;
fn main() {
let stdin = io::stdin();
for line in stdin.lock().lines() {
println!("{}", line.unwrap());
}
}
Rouille 0.10–0.12 (voir documentation ):
use std::io;
fn main() {
for line in io::stdin().lines() {
print!("{}", line.unwrap());
}
}
Rust 0.9 (voir 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);
}
}
Rouille 0.8:
use std::io;
fn main() {
let lines = io::stdin().read_lines();
for line in lines.iter() {
println(*line);
}
}
Rouille 0.7:
use std::io;
fn main() {
let lines = io::stdin().read_lines();
for lines.iter().advance |line| {
println(*line);
}
}
Au 17 avril 2015 à partir de mdcox
sur la 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);
}
}
La question est de lire les lignes de stdin et de retourner un vecteur. Sur la rouille 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
}
Note de l'éditeur: cette réponse est antérieure à Rust 1.0. S'il vous plaît voir les autres réponses pour des solutions modernes.
Euh ... Après de nombreuses tentatives et erreurs, j'ai trouvé une solution.
J'aimerais toujours voir une meilleure solution afin que je ne vais pas accepter ma propre solution.
Le code ci-dessous imprime exactement ce que l'utilisateur a saisi.
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));
}
}
Je peux penser à peu de choses.
Lire toutes les entrées dans single String
let mut input = String::new();
io::stdin().read_to_end(&mut input);
Lire les lignes dans Vector
. Celui-ci n'a pas panic
lorsque la lecture d'une ligne échoue, mais ignore cette ligne qui a échoué.
let stdin = io::stdin();
let locked = stdin.lock();
let v: Vec<String> = locked.lines().filter_map(|line| line.ok()).collect();
De plus si vous voulez l’analyser:
Après l'avoir lu en chaîne, faites ceci. Vous pouvez l'analyser dans d'autres collections implémentant FromIterator
. Les éléments contenus dans la collection doivent également implémenter FromStr
. Tant que la contrainte de trait satisfait, vous pouvez changer Vec en n'importe quel Collection:FromIterator
, Collection<T: FromStr>
let v: Vec<i32> = "4 -42 232".split_whitespace().filter_map(|w| w.parse().ok()).collect();
Aussi, vous pouvez l'utiliser sur la 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();
C’est ce que j’ai proposé (avec l’aide des sympathiques personnes de la chaîne #Rust IRC sur 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));
}
}
Et la preuve que ça marche :)
$ rustc readlines.rs
$ echo -en 'this\nis\na\ntest' | ./readlines
Line #1: this
Line #2: is
Line #3: a
Line #4: test
Dans Rust 1.0 et versions ultérieures, vous pouvez utiliser la méthode lines
pour tout ce qui implémente le std::io::BufRead
trait pour obtenir un itérateur sur les lignes de l'entrée. Vous pouvez également utiliser read_line
, mais il est plus probable que vous utilisiez l'itérateur. Voici une version de la fonction dans la question en utilisant des itérateurs. voir ci-dessous pour une explication plus détaillée. ( lien de terrain de jeu )
use std::io;
use std::io::prelude::*;
pub fn read_lines() -> Vec<String> {
let stdin = io::stdin();
let stdin_lock = stdin.lock();
let vec = stdin_lock.lines().filter_map(|l| l.ok()).collect();
vec
}
Et voici une version qui ressemble plus à la version C++ de la question, mais qui n’est pas vraiment la manière idiomatique de faire cela dans Rust ( playground ):
use std::io;
use std::io::prelude::*;
pub fn read_lines() -> Vec<String> {
let mut vec = Vec::new();
let mut string = String::new();
let stdin = io::stdin();
let mut stdin_lock = stdin.lock();
while let Ok(len) = stdin_lock.read_line(&mut string) {
if len > 0 {
vec.Push(string);
string = String::new();
} else {
break
}
}
vec
}
Pour obtenir quelque chose qui implémente BufRead
, nécessaire pour appeler lines()
ou read_line()
, appelez std::io::stdin()
pour obtenir un descripteur en entrée standard, puis appelez lock()
pour obtenir le contrôle exclusif du résultat. flux d'entrée standard (vous devez disposer du contrôle exclusif pour obtenir une variable BufRead
, car sinon la mise en mémoire tampon pourrait produire des résultats arbitraires si deux threads lisaient à la fois à partir de stdin).
Pour collecter le résultat dans un Vec<String>
, vous pouvez utiliser la méthode collect
sur un itérateur. lines()
renvoie un itérateur sur Result<String>
, nous devons donc gérer les cas d'erreur dans lesquels une ligne n'a pas pu être lue. pour cet exemple, nous ignorons simplement les erreurs avec un filter_map
qui ignore toutes les erreurs.
La version de type C++ utilise read_line
, qui ajoute la ligne de lecture à une chaîne donnée, puis nous poussons la chaîne dans notre Vec
. Comme nous transférons la propriété de la chaîne à la Vec
, et que read_line
continue d’ajouter à la string
, nous devons allouer une nouvelle chaîne pour chaque boucle (cela semble être un bogue de la version C++ d’origine dans la version précédente). question, dans laquelle la même chaîne est partagée et continuera donc à accumuler chaque ligne). Nous utilisons while let
pour continuer à lire jusqu’à ce que nous rencontrions une erreur, et nous cassons si nous lisons un jour zéro octet qui indique la fin de l’entrée.