【问题标题】:How can I convert a string of numbers to an array or vector of integers in Rust?如何在 Rust 中将一串数字转换为数组或整数向量?
【发布时间】:2014-12-19 15:20:50
【问题描述】:

我正在 STDIN 上写入一串数字(例如 4 10 30 232312),我想读取它并将其转换为整数数组(或向量),但我找不到正确的方法。到目前为止,我有:

use std::io;

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

【问题讨论】:

    标签: arrays string rust converters


    【解决方案1】:

    你可以这样做:

    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&lt;io::Result&lt;String&gt;&gt;,因此要获得String,您需要unwrap() 两次。

    然后我们用空格分割它并从多余的空白中修剪结果块 (2),删除修剪后留下的空块 (3),将字符串转换为 i32s (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() 函数参数checks for different kinds of whitespace, not only space characters

    【讨论】:

    • 谢谢你,弗拉基米尔,你拯救了这一天。顺便说一句,哇,Rust 真的很激烈!
    • 还有一种方法可以使用正则表达式按任意空格进行分割。它可能会提供更简洁的代码,但它需要使用非标准板条箱(尽管它包含在发行版中)。 here 的例子几乎就是你想要的。
    • 您也可以只使用.words()(不过比空格更通用)。
    • @VladimirMatveev 在今天的 Rust 版本中实现这一目标的最佳方法是什么?我一直试图在网上拼凑这些碎片,但似乎可用的信息不是最新的。谢谢你:)
    • @DavidDias,在这个答案的最新版本中没有read_line();我建议改用lines().next()
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      更安全的版本。这个跳过失败的解析,这样失败的展开就不会恐慌。 使用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.
      

      您甚至可以像这样阅读 Vector of Vectors。

      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&lt;T&gt; 并过滤掉所有 Nones 的闭包。

      ok()Result&lt;R,E&gt; 变为Option&lt;R&gt;,以便在这种情况下过滤错误。

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 2021-06-14
          • 2017-09-16
          • 2011-10-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多