我打算制作一个分词器。我需要阅读用户输入的每一行,并在用户按下 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 中仍然有效。
在 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());
}
}
截至 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);
}
}
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
继续读取,直到遇到错误,如果读取到零字节(表示输入结束),我们就会中断。
问题是从标准输入读取行并返回一个向量。在 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
}
我能想到的方法很少。
将所有输入读入单个
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();
在 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。请参阅现代解决方案的其他答案。
呃...经过多次尝试和错误,我找到了解决方案。
我仍然希望看到更好的解决方案,所以我不会接受我自己的解决方案。
下面的代码准确打印用户输入的内容。
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。请参阅现代解决方案的其他答案。
来自 处理猜测
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);
// ...
从 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();