如何在 Rust 中读取用户输入?

问题描述 投票:0回答:9

我打算制作一个分词器。我需要阅读用户输入的每一行,并在用户按下 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;
}

这个问题指的是 Rust 1.0 之前的 Rust 部分,但一般概念在 Rust 1.0 中仍然有效。

input io rust
9个回答
168
投票

在 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());
    }
}

14
投票

截至 2015 年 4 月 17 日,来自 Firefox Rust

IRC
频道上的 mdcox

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
投票

在 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 中执行此操作的惯用方法(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
}

要获取实现

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
继续读取,直到遇到错误,如果读取到零字节(表示输入结束),我们就会中断。


3
投票

问题是从标准输入读取行并返回一个向量。在 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
}

3
投票

我能想到的方法很少。

将所有输入读入单个

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();

2
投票

在 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));
    }
}

这个答案早于 Rust 1.0。请参阅现代解决方案的其他答案。


2
投票

呃...经过多次尝试和错误,我找到了解决方案。

我仍然希望看到更好的解决方案,所以我不会接受我自己的解决方案。

下面的代码准确打印用户输入的内容。

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 1.0。请参阅现代解决方案的其他答案。


1
投票

来自 处理猜测

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);
}

虽然 Rust 有很多不同的方法可以实现这一点,但我认为这是最简单的实现方法。

use std::io;
// ...
    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("Failed to read line");

    println!("Your input was `{}`", input);
// ...

1
投票

从 Rust 1.62 开始,读取用户输入行比以前简单一些。现在

Stdin::lines()
已经稳定,不再需要担心锁定,因为
lines()
会为您处理好这个问题:

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

如果您想构建

Vec
String
,新方法可以让您在一行中完成此操作:

let inputs: Vec<_> = std::io::stdin().lines().map(|line| line.unwrap()).collect();
© www.soinside.com 2019 - 2024. All rights reserved.