如何在 Rust 1.0 中从用户读取整数输入?

33

已经存在的答案都是基于 from_str 的(比如 高效地从控制台读取用户输入),但显然在 Rust 1.0 中 from_str(x) 已经改变成了 x.parse()。对于新手而言,不清楚原始解决方案应该如何适应这个变化。

截至 Rust 1.0,从用户那里获取整数输入的最简单方法是什么?

8个回答

69

这是一个带有所有可选类型注释和错误处理的版本,对像我这样的初学者可能很有用:

use std::io;

fn main() {
    let mut input_text = String::new();
    io::stdin()
        .read_line(&mut input_text)
        .expect("failed to read from stdin");

    let trimmed = input_text.trim();
    match trimmed.parse::<u32>() {
        Ok(i) => println!("your integer input: {}", i),
        Err(..) => println!("this was not an integer: {}", trimmed),
    };
}

使用unwrap()怎么样?如何处理解析错误并将其报告到stderr? - zero_cool
@zero_cool 错误是通过处理由parse()产生的Resultmatch来报告的。unwrap()会导致程序崩溃。 - raindev
这个程序“读取一行并将整行解析为整数”,而不是“读取下一个单独的整数”,这与C++中的cin >> i有很大不同。 - ynn

24

如果您想在像 Codeforces 这样的网站上进行竞技编程,但无法访问 text_io,那么这个解决方案就适用于您。

我使用以下宏从 stdin 中读取不同的值:


#[allow(unused_macros)]
macro_rules! read {
    ($out:ident as $type:ty) => {
        let mut inner = String::new();
        std::io::stdin().read_line(&mut inner).expect("A String");
        let $out = inner.trim().parse::<$type>().expect("Parsable");
    };
}

#[allow(unused_macros)]
macro_rules! read_str {
    ($out:ident) => {
        let mut inner = String::new();
        std::io::stdin().read_line(&mut inner).expect("A String");
        let $out = inner.trim();
    };
}

#[allow(unused_macros)]
macro_rules! read_vec {
    ($out:ident as $type:ty) => {
        let mut inner = String::new();
        std::io::stdin().read_line(&mut inner).unwrap();
        let $out = inner
            .trim()
            .split_whitespace()
            .map(|s| s.parse::<$type>().unwrap())
            .collect::<Vec<$type>>();
    };
}
  

使用方法如下:


fn main(){
   read!(x as u32);
   read!(y as f64);
   read!(z as char);
   println!("{} {} {}", x, y, z);

   read_vec!(v as u32); // Reads space separated integers and stops when newline is encountered.
   println!("{:?}", v);
}


太棒了!我找了三个月才找到这个。 - evening_g

14

我认为如果您只需要一次读取一个值,则它可以在稳定版本上运行。 - Vladimir Matveev
1
这里是 text_io 的开发者。@VladimirMatveev 是正确的,如果你只读取单个值而不是元组,那么 text_io 可以在稳定版上运行。实际上,要读取多个值需要使用 nightly 版本。我会更新 Github 描述。 - oli_obk
现在有更好的选择吗? Rust发布3年后,我认为会有更好的方法将值输入整数变量。建议的解决方案是我在Rust书中遇到的,但它很冗长,感觉对于输入有些过度。text_io建议很好,但是在使用read!()之前无法从函数中打印语句。 - JosephTLyons
@joe_04_04 你是什么意思,"在使用read!()之前从函数中打印语句"? - Daniel Fath
@DanielFath,如果我在尝试使用text_ioread!()之前使用println!(),那么应该从println!()打印的项目直到read!()方法之后才被打印。 - JosephTLyons

14

以下是一些可能性(Rust 1.7):

use std::io;

fn main() {
    let mut n = String::new();
    io::stdin()
        .read_line(&mut n)
        .expect("failed to read input.");
    let n: i32 = n.trim().parse().expect("invalid input");
    println!("{:?}", n);

    let mut n = String::new();
    io::stdin()
        .read_line(&mut n)
        .expect("failed to read input.");
    let n = n.trim().parse::<i32>().expect("invalid input");
    println!("{:?}", n);

    let mut n = String::new();
    io::stdin()
        .read_line(&mut n)
        .expect("failed to read input.");
    if let Ok(n) = n.trim().parse::<i32>() {
        println!("{:?}", n);
    }
}

这些可以帮助你避免使用额外的库而不需要进行模式匹配的繁琐过程。


4
加油,这很明显不同,完整的回答会更容易让读者理解。 - qed
对我来说,它看起来与被接受的答案非常相似。 - Shepmaster
“解析部分更简洁” - 这是一条类似于“使用Option::expect而不是match可以更简洁并在错误情况下失败”的评论。 “完整的答案使读者更容易理解” - 这个答案缺少所需的use std::iofn main,这是被接受的答案所具备的,对于真正的初学者来说这是必要的。 - Shepmaster
没错,挺公平的。 - qed
2
为了清楚明了起见,您可以留下答案,其他人可能会发现它有用并点赞。只要有适当的描述来说明为什么它不同且更好,那就是一个有效的答案! - Shepmaster
显示剩余2条评论

4

parse 差不多是一样的;现在不好处理的是 read_line

use std::io;

fn main() {
    let mut s = String::new();
    io::stdin().read_line(&mut s).unwrap();

    match s.trim_right().parse::<i32>() {
        Ok(i) => println!("{} + 5 = {}", i, i + 5),
        Err(_) => println!("Invalid number."),
    }
}

3

如果您想要简单的语法,可以创建扩展方法:

use std::error::Error;
use std::io;
use std::str::FromStr;

trait Input {
    fn my_read<T>(&mut self) -> io::Result<T>
    where
        T: FromStr,
        T::Err: Error + Send + Sync + 'static;
}

impl<R> Input for R where R: io::Read {
    fn my_read<T>(&mut self) -> io::Result<T>
    where
        T: FromStr,
        T::Err: Error + Send + Sync + 'static,
    {
        let mut buff = String::new();
        self.read_to_string(&mut buff)?;

        buff.trim()
            .parse()
            .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
    }
}

// Usage:

fn main() -> io::Result<()> {
    let input: i32 = io::stdin().my_read()?;

    println!("{}", input);

    Ok(())
}

2
你可以尝试使用这段代码。
fn main() {

    let mut line  = String::new();

    // read input line string and store it into line
    std::io::stdin().read_line(&mut line).unwrap();

    // convert line to integer
    let number : i32 = line.trim().parse().unwrap();

    println!("Your number {}",number);
}

现在,您可以编写一个函数来获取用户输入,并像下面这样每次使用它。
fn main() {

    let first_number = get_input();
    let second_number = get_input();

    println!("Summation : {}",first_number+second_number);

}

fn get_input() -> i32{

    let mut line  = String::new();
    std::io::stdin().read_line(&mut line).unwrap();
    let number : i32 = line.trim().parse().unwrap();
    return number ;
}

有没有一种方法可以使其适用于任何数据类型,而不是为每种类型创建一个函数? - evening_g

2

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