What's the easiest way to read several ints fr

2019-06-16 15:20发布

Suppose I expect a line with 3 integers from stdin. What's the easiest way to read and parse them? What's the Rust equivalent of a, b, c = map(int, input().split()) in Python or scanf("%d %d %d", &a, &b, &c); in C?

The best way I came up with was something like:

let mut line = String::new();
io::stdin().read_line(&mut line).unwrap();
let parts: Vec<&str> = line.split_whitespace().collect();
let a: i32 = parts[0].parse().unwrap();
let b: i32 = parts[1].parse().unwrap();
let c: i32 = parts[2].parse().unwrap();

Is there a simpler way?

标签: rust
3条回答
再贱就再见
2楼-- · 2019-06-16 15:50

You can use scan-rules for this:

/*!
Add this to your `Cargo.toml`, or just run with `cargo script`:

```cargo
[dependencies]
scan-rules = "0.1.1"
```
*/
#[macro_use] extern crate scan_rules;

fn main() {
    print!("Enter 3 ints: ");
    readln! {
        (let a: i32, let b: i32, let c: i32) => {
            println!("a, b, c: {}, {}, {}", a, b, c);
        }
    }
}

If you want to do something a little more involved, you can use multiple rules and type inference, and specify what to do if the input doesn't match any of the rules given (by default it panic!s):

    readln! {
        // Space-separated ints
        (let a: i32, let b: i32, let c: i32) => {
            println!("a b c: {} {} {}", a, b, c);
        },

        // Comma-separated ints, using inference.
        (let a, ",", let b, ",", let c) => {
            let a: i32 = a;
            let b: i32 = b;
            let c: i32 = c;
            println!("a, b, c: {}, {}, {}", a, b, c);
        },

        // Comma-separated list of *between* 1 and 3 integers.
        ([let ns: i32],{1,3}) => {
            println!("ns: {:?}", ns);
        },

        // Fallback if none of the above match.
        (..line) => {
            println!("Invalid input: {:?}", line);
        }
    }

Disclaimer: I am the author of scan-rules.

查看更多
【Aperson】
3楼-- · 2019-06-16 15:52

I am new to Rust so I may not have all this down exactly but I have made a solution. I discovered you can use split_white_space to put the integers in the string into an iterator. Then you unwrap it out of std::option::Option<&str> using ".unwrap()". After parse the &str using ".parse()" and unwrap its result using ".unwrap()". Then you have an int if you have specified the type of variable using "variable_name: i32 =...". Checkout what I did:

let mut numbers = String::new();
io::stdin()
    .read_line(&mut numbers)
    .ok()
    .expect("read error");
let mut iter = numbers.split_whitespace();
let mut x: i32 = iter.next().unwrap().parse().unwrap();
let mut y: i32 = iter.next().unwrap().parse().unwrap();
let mut n: i32 = iter.next().unwrap().parse().unwrap();
println!("{},{},{}", x, y, n);
查看更多
ら.Afraid
4楼-- · 2019-06-16 16:02

You can use text_io for this:

#[macro_use] extern crate text_io;

fn main() {
    // reads until a whitespace is encountered
    let a: i32 = read!();
    let b: i32 = read!();
    let c: i32 = read!();
}

text_io 0.1.3 also supports a scan! macro:

let (a, b, c): (i32, i32, i32);
scan!("{}, {}, {}\n", a, b, c);

in case you want to read from a file or some other source, you can also use both macros on any type that implements Iterator<Item=u8>:

use std::io::Read;
let mut file = std::fs::File::open("text.txt").unwrap()
                                              .bytes()
                                              .map(Result::unwrap);
let x: i32 = read!("{}\n", file);

or

let (x, y, z): (i32, i32, i32);
scan!(file => "{}, {}: {}", x, y, z);

You can leave off the : i32s if the compiler can infer those types from context.

Disclaimer: I am the author of text_io.

查看更多
登录 后发表回答