Convert a String of numbers to an Array/Vector of ints in Rust

隐身守侯 提交于 2019-11-30 12:56:03

Updated for Rust 1.x

You can do something like this:

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

First, we take a lock of the stdin which lets you work with stdin as a buffered reader (by default stdin in Rust is unbuffered; you need to call lock() method on it to obtain a buffered version of it, but this buffered version is only one for all threads in your program, hence the access to it should be synchronized).

Next, we read the next line (1); I'm using lines() iterator whose next() method returns Option<io::Result<String>>, therefore to obtain just String you need to unwrap() twice.

Then we split it by spaces and trim resulting chunks from extra whitespace (2), remove empty chunks which were left after trimming (3), convert strings to i32s (4) and collect the result to a vector (5).

We also need to import std::io::BufRead trait (a) in order to use lines() method.

If you know in advance that your input won't contain more than one space between numbers, you can omit step (3) and move trim() call from (2) to (1):

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

Update

Rust, however, already provides a method to split a string into a sequence of whitespace-separated words, called split_whitespace():

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

split_whitespace() is in fact just a combination of split() and filter(), just like in my original example. It uses a function in split() argument though which checks for different kinds of whitespace, not only space characters. You can find its source is here.

On Rust 1.5.x, a working solution is:

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().unwrap())
        .collect();

    for num in numbers {
        println!("{}", num);
    }
}

Safer version. This one skips failed parses so that failed unwrap doesn't panic. Use read_line for reading single 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().filter_map(|w| w.parse().ok()).collect();

You can even read Vector of Vectors like this.

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

Above one works for inputs like

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

then turns them it into

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

filter_map accepts a closure that returns Option<T> and filters out all Nones.

ok() turns Result<R,E> to Option<R> so that errors can be filtered in this case.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!