如何在Rust中将数字字符串转换为整数数组或向量?

28
我正在使用STDIN输入一串数字(例如4 10 30 232312),我想要读取并转换为整数数组(或向量),但我找不到正确的方法。目前为止,我的代码是:

我正在使用STDIN输入一串数字(例如4 10 30 232312),我想要读取并转换为整数数组(或向量),但我找不到正确的方法。目前为止,我的代码是:

use std::io;

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

36

你可以像这样做:

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>>,因此要仅获取String,您需要unwrap()两次。

然后,我们通过空格拆分它并修剪结果块的额外空格(2),移除在修剪之后留下的空块(3),将字符串转换为i32(4)并将结果收集到向量中(5)。

我们还需要导入std::io::BufRead trait(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() 实际上只是我原来示例中 split()filter() 的结合。它使用了一个 split() 函数参数,该参数检查不同种类的空格字符,而不仅仅是空格字符


1
还有一种使用正则表达式来按任意空格分割的方法。这可能会给出更简洁的代码,但需要使用非标准的包(尽管它已包含在发行版中)。这里的示例几乎是您想要的。 - Vladimir Matveev
4
你也可以使用 .words() 方法(比只使用空格稍微更通用一些)。 - huon
1
@VladimirMatveev,在当今的Rust版本中实现这一点最好的方法是什么?我一直在尝试在网上收集相关信息,但似乎可用的信息不是最新的。谢谢 :) - David Dias
@DavidDias,我已经更新了答案。它与旧版本并没有太大的区别;唯一改变的是从stdin读取一行的方式以及现在调用split_whitespace()而不是words()。当然,现在也没有int了。 - Vladimir Matveev
1
@DavidDias,在最新版本的答案中没有read_line();我建议使用lines().next()代替。 - Vladimir Matveev
显示剩余3条评论

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

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.

你甚至可以像这样读取向量的向量。
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>的闭包,并过滤掉所有的None

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


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接