Как прочитать пользовательский ввод в 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);
// ...