Как прочитать пользовательский ввод в Rust?
Примечание редактора: этот вопрос относится к частям ржавчины, которые предшествуют ржавчине 1.0, но общая концепция по-прежнему действительна в Rust 1.0.
Я намерен сделать токенизатор. Мне нужно прочитать каждую строку типа пользователя и остановить чтение, как только пользователь нажимает ctrl-D.
Я искал вокруг и нашел только пример на Rust IO, который даже не компилируется. Я посмотрел на модуль и обнаружил, что
8 ответов
Примечание редактора: этот ответ предшествует Rust 1.0. См. другие ответы для современных решений.
в Rust 0.4, используйте ReaderUtil
черта к
ржавчина 1.x (см. документация):
use std::io;
use std::io::prelude::*;
fn main() {
let stdin = io::stdin();
for line in stdin.lock().lines() {
println!("{}", line.unwrap());
}
}
Руст 0.10–0.12 (см. документация):
use std::io;
fn main() {
for line in io::stdin().lines() {
print!("{}", line.unwrap());
}
}
ржавчине 0.9 (см. документация 0.9):
use std::io;
use std::io::buffered::BufferedReader;
fn main() {
let mut reader = BufferedReader::new(io::stdin());
for line in reader.lines() {
print(line);
}
}
Руст 0.8:
use std::io;
fn main() {
let lines = io::stdin().read_lines();
for line in lines.iter() {
println(*line);
}
}
Руст 0.7:
use std::io;
fn main() {
let lines = io::stdin().read_lines();
for lines.iter().advance |line| {
println(*line);
}
}
по состоянию на 17 апреля 2015 от mdcox
на 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);
}
}
вопрос в том, чтобы прочитать строки из stdin и вернуть вектор. На Ржавчину 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
}
Примечание редактора: этот ответ предшествует Rust 1.0. См. другие ответы для современных решений.
эээ... После многих проб и ошибок, я нашел решение.
Я все равно хотел бы увидеть лучшее решение, поэтому я не собираюсь принимать свое собственное решение.
код ниже печатает именно то, что пользователь вводит.
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));
}
}
это то, что я придумал (с некоторой помощью от дружелюбных людей в канале # rust IRC на 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));
}
}
и доказательство того, что он работает:)
$ rustc readlines.rs
$ echo -en 'this\nis\na\ntest' | ./readlines
Line #1: this
Line #2: is
Line #3: a
Line #4: test
есть несколько способов, которые я могу придумать.
прочитайте все входные данные в single String
let mut input = String::new();
io::stdin().read_to_end(&mut input);
читать строки в Vector
. Этот не panic
при чтении строки не удается, вместо этого он пропускает, что не линия.
let stdin = io::stdin();
let locked = stdin.lock();
let v: Vec<String> = locked.lines().filter_map(|line| line.ok()).collect();
кроме того, если вы хотите, чтобы разобрать его:
после прочтения его в строку этого. Вы можете разобрать его на другие коллекции, которые реализуют FromIterator
. Содержащиеся в коллекции элементы также должны реализовываться FromStr
. Пока ограничение признака удовлетворяет, вы можете изменить Vec на any Collection:FromIterator
, Collection<T: FromStr>
let v: Vec<i32> = "4 -42 232".split_whitespace().filter_map(|w| w.parse().ok()).collect();
также вы можете использовать его на 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();
в Rust 1.0 и более поздних, вы можете использовать lines
способ на все, что реализует std::io::BufRead
черта для получения итератора по строкам на входе. Вы также можете использовать read_line
, но использование итератора более вероятно, что вы хотите. Вот версия функции в вопросе с использованием итераторов; более подробное объяснение см. ниже. (площадка ссылке)
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
}
а вот версия это больше похоже на версию C++ в вопросе, но на самом деле это не идиоматический способ сделать это в Rust (детская площадка):
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
}
получить что-то, что реализует BufRead
, который необходимо вызвать lines()
или read_line()
, вы называете std::io::stdin()
получить дескриптор стандартного ввода, а затем вызвать lock()
в результате этого, чтобы получить эксклюзивный контроль над стандартным входным потоком (вы должны иметь эксклюзивный контроль для получения BufRead
, потому что в противном случае буферизация может привести к произвольным результатам, если два потока читали из stdin сразу).
чтобы собрать результат в Vec<String>
можно использовать collect
метод итератора. lines()
возвращает итератор над Result<String>
, поэтому нам нужно обрабатывать случаи ошибок, в которых строка не может быть прочитана; для этого примера мы просто игнорируем ошибки с filter_map
это просто пропускает любые ошибки.
версия C++ like использует read_line
, который добавляет строку чтения к данной строке, а затем мы нажимаем строку в наш Vec
. Потому что мы передаем право собственности на строку Vec
когда мы это делаем, и потому read_line
в противном случае продолжал бы добавлять к string
, нам нужно выделить новую строку для каждого цикла (это похоже на ошибку в исходной версии C++ в вопросе, в которой одна и та же строка является общей и поэтому будет накапливать каждую строку). Мы используем while let
продолжать читайте, пока мы не нажмем ошибку, и мы сломаемся, если мы когда-либо читали нулевые байты, что указывает на конец ввода.