Как прочитать пользовательский ввод в Rust?
Примечание редактора: этот вопрос относится к частям Rust, предшествующим Rust 1.0, но общая концепция по-прежнему действует в Rust 1.0.
Я собираюсь сделать токенизатор. Мне нужно прочитать каждую строку, которую пользователь вводит, и прекратить чтение, когда пользователь нажимает ctrl - D.
Я искал вокруг и нашел только один пример на Rust IO, который даже не компилируется. Я посмотрел документацию к модулю io
и обнаружил, что функция read_line()
является частью интерфейса ReaderUtil
, но вместо этого stdin()
возвращает Reader
.
Код, который мне нужен, по сути будет выглядеть следующим образом в C++
vector<string> readLines () {
vector<string> allLines;
string line;
while (cin >> line) {
allLines.push_back(line);
}
return allLines;
}
Ответы
Ответ 1
Примечание редактора: этот ответ предшествует Rust 1.0. Пожалуйста, посмотрите другие ответы для современных решений.
В Rust 0.4 используйте черту ReaderUtil
для доступа к функции read_line
. Обратите внимание, что вам нужно явно привести значение к типу черты, например, 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));
}
}
Ответ 2
Rust 1.x(см. документация):
use std::io;
use std::io::prelude::*;
fn main() {
let stdin = io::stdin();
for line in stdin.lock().lines() {
println!("{}", line.unwrap());
}
}
Rust 0.10-0.12 (см. документация):
use std::io;
fn main() {
for line in io::stdin().lines() {
print!("{}", line.unwrap());
}
}
Rust 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);
}
}
Ответ 3
По состоянию на 17 апреля 2015 г. из mdcox
на мозаику ржавчина 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);
}
}
Ответ 4
Вопрос заключается в том, чтобы читать строки из stdin и возвращать вектор. В 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
}
Ответ 5
Есть несколько способов, о которых я могу думать.
Прочитайте весь вход в одиночный 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 на любые 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();
Ответ 6
Примечание редактора: этот ответ предшествует 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));
}
}
Ответ 7
В 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++, использует read_line
, который добавляет строку чтения к заданной строке, а затем мы помещаем строку в наш Vec
. Поскольку мы передаем право собственности на строку в Vec
, когда мы делаем это, и потому что read_line
в противном случае продолжит добавлять к string
, нам нужно выделить новую строку для каждого цикла (это, похоже, ошибка в оригинальная версия C++ в вопросе, в которой одна и та же строка является общей и будет продолжать накапливать каждую строку). Мы используем while let
для продолжения чтения до тех пор, пока не обнаружим ошибку, и мы прерываемся, если мы когда-либо читаем нулевые байты, которые указывают на конец ввода.
Ответ 8
Это то, что я придумал (с помощью дружественных людей в IRC-канале #rust на 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
Ответ 9
от https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html#processing-a-guess
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("Failed to read line");
println!("You guessed: {}", guess);
}
Хотя есть много разных способов сделать это в ржавчине, я думаю, что это самый простой для реализации.
use std::io;
// ...
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Failed to read line");
println!("Your input was '{}'", input);
// ...