23

我在 STDIN 上写了一串数字(例如4 10 30 232312),我想读取它并转换为整数数组(或向量),但我找不到正确的方法。到目前为止,我有:

use std::io;

fn main() {
    let mut reader = io::stdin();
    let numbers = reader.read_line().unwrap();
}
4

4 回答 4

32

你可以这样做:

use std::io::{self, BufRead};                   // (a)

fn main() {
    let reader = io::stdin();
    let numbers: Vec<i32> = 
        reader.lock()                           // (0)
              .lines().next().unwrap().unwrap() // (1)
              .split(' ').map(|s| s.trim())     // (2)
              .filter(|s| !s.is_empty())        // (3)
              .map(|s| s.parse().unwrap())      // (4)
              .collect();                       // (5)
    println!("{:?}", numbers);
}

首先,我们锁定标准输入,让您可以将标准输入用作缓冲阅读器。默认情况下,Rust 中的标准输入是无缓冲的;您需要调用该lock()方法来获取它的缓冲版本,但是这个缓冲版本是程序中所有线程的唯一版本,因此应该同步对它的访问。

接下来,我们阅读下一行(1);我正在使用其方法返回的lines()迭代器,因此只需两次即可获得。next()Option<io::Result<String>>Stringunwrap()

然后我们用空格分割它并从额外的空白中修剪结果块 (2),删除修剪后留下的空块 (3),将字符串转换为i32s (4) 并将结果收集到向量 (5)。

我们还需要导入std::io::BufReadtrait (a) 才能使用该lines()方法。

如果您事先知道您的输入不会在数字之间包含一个以上的空格,您可以省略步骤 (3) 并将trim()调用从 (2) 移动到 (1):

let numbers: Vec<i32> = 
    reader.lock()
          .lines().next().unwrap().unwrap()
          .trim().split(' ')
          .map(|s| s.parse().unwrap())
          .collect();

Rust 还提供了一种将字符串拆分为一系列以空格分隔的单词的方法,称为split_whitespace()

let numbers: Vec<i32> =
    reader.read_line().unwrap().as_slice()
        .split_whitespace()
        .map(|s| s.parse().unwrap())
        .collect()

split_whitespace()实际上只是 and 的组合split()filter()就像我原来的例子一样。它使用一个split()函数参数来检查不同类型的空格,而不仅仅是空格字符

于 2014-10-23T21:01:14.760 回答
16

在 Rust 1.5.x 上,一个可行的解决方案是:

fn main() {
    let mut numbers = String::new();

    io::stdin()
        .read_line(&mut numbers)
        .ok()
        .expect("read error");

    let numbers: Vec<i32> = numbers
        .split_whitespace()
        .map(|s| s.parse().expect("parse error"))
        .collect();

    for num in numbers {
        println!("{}", num);
    }
}
于 2015-12-26T15:15:30.310 回答
8

更安全的版本。这个跳过失败的解析,这样失败的展开就不会恐慌。用于read_line读取单行。

let mut buf = String::new();

// use read_line for reading single line 
std::io::stdin().read_to_string(&mut buf).expect("");

// this one skips failed parses so that failed unwrap doesn't panic
let v: Vec<i32> = buf
    .split_whitespace() // split string into words by whitespace
    .filter_map(|w| w.parse().ok()) // calling ok() turns Result to Option so that filter_map can discard None values
    .collect(); // collect items into Vector. This determined by type annotation.

你甚至可以像这样阅读 Vector of Vectors。

let stdin = io::stdin();
let locked = stdin.lock();
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 424 -42 124
42 242 23 22 241
24 12 3 232 445

然后把它们变成

[[2, 424, -42, 124],
[42, 242, 23, 22, 241],
[24, 12, 3, 232, 445]]

filter_map接受一个返回Option<T>并过滤掉所有Nones 的闭包。

ok()转向Result<R,E>以便Option<R>在这种情况下可以过滤错误。

于 2016-10-29T01:17:44.533 回答
0

更安全的版本Dulguun Otgon只是跳过所有错误。如果您不想跳过错误,请考虑使用下一种方法。

fn parse_to_vec<'a, T, It>(it: It) -> Result<Vec<T>, <T as FromStr>::Err>
where
    T: FromStr,
    It: Iterator<Item = &'a str>,
{
    it.map(|v| v.parse::<T>()).fold(Ok(Vec::new()), |vals, v| {
        vals.and_then(|mut vals| {
            v.and_then(|v| { 
                vals.push(v);
                Ok(vals)
            })
        })
    })
}    

在使用它时,您可以按照通常的恐慌方式expect

let numbers = parse_to_vec::<i32, _>(data_str.trim().split(" "))
    .expect("can't parse data");

或更智能的方式转换为结果

let numbers = parse_to_vec::<i32, _>(data_str.trim().split(" "))
    .map_err(|e| format!("can't parse data: {:?}", e))?;
于 2021-10-22T03:22:46.037 回答