Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extract two integers from a string?

Tags:

input

rust

How do I extract two i32s from a single line of input in Rust? In Python I can read two ints like:

a, b = map(int, input().split()) #  "2 3"  =>  a=2 and b=3

As of Rust 1.3.0, I can run the following to read one i32:

let mut s = String::new();
std::io::stdin().read_line(&mut s).ok().expect("read_line panic");
let n: i32 = s.trim().parse().ok().expect("parse panic");
like image 874
Jay Foreman Avatar asked Nov 10 '15 12:11

Jay Foreman


2 Answers

&str has a split_whitespace() method which returns an iterator yielding parts of the target string separated by an arbitrary amount of whitespace, much like split() in Python. You can use the map() iterator adapter to convert each part to a number:

let mut s = String::new();
std::io::stdin().read_line(&mut s).expect("read_line error");

let mut parts = s.split_whitespace().map(|s| s.parse::<i32>());
match (parts.next(), parts.next()) {
    (Some(Ok(a)), Some(Ok(b))) => {
        // a and b are i32
    }
    // handle other problems: not enough numbers, numbers are invalid, etc
    _ => {}  // ignore invalid input
}

This looks a lot more verbose than the Python version, but that's mostly because in Python all errors will be thrown as exceptions, while in Rust you have to handle them explicitly. If you don't care about them, you can throw match away:

let a = parts.next().unwrap().unwrap();
let b = parts.next().unwrap().unwrap();

Even another approach would be to use a handy collect() implementation for Result<Vec<_>, _>:

let items: Result<Vec<i32>, _> = parts.collect();

This way if any of numbers in the input string fail to parse, items will contain the corresponding Err value, but if they all are parsed successfully, then items will contain Ok variant with the vector of parsed numbers. With this approach you also do not need to specify ::<i32>() in parse() invocation, as it will be inferred automatically (no need for mut either):

let parts = s.split_whitespace().map(|s| s.parse());

Also there is no one-liner function to read a string from stdin in the standard library. It is somewhat unfortunate but rarely a problem in practice. There are libraries which provide such functionality; see other answers to find some examples.

like image 57
Vladimir Matveev Avatar answered Sep 29 '22 19:09

Vladimir Matveev


You can use the text_io crate which offers a macro read! to read from standard input to produce tuples:

#[macro_use] extern crate text_io;
let (a, b): (i32, i32);
scan!("{} {}", a, b);

You can also read the values one by one

#[macro_use] extern crate text_io;
let a: i32 = read!();
let b: i32 = read!();
like image 25
oli_obk Avatar answered Sep 29 '22 21:09

oli_obk