如何将数字字符串转换为数字向量?

3
我正在尝试将数字的字符串(例如12345)存储到向量中,使得向量包含{1,2,3,4,5}。
由于我对Rust完全是新手,我在处理类型(String、str、char等)以及缺乏任何有关转换的信息时遇到了问题。
我的当前代码如下:
fn main() {
    let text = "731671";                
    let mut v: Vec<i32>;
    let mut d = text.chars();
    for i in 0..text.len() {
        v.push( d.next().to_digit(10) );
    }
}

1
思考的食物 - ildjarn
4个回答

9
你离成功不远了!
首先,索引循环 for i in 0..text.len() 并不必要,因为你将使用迭代器。更简单的方法是直接在迭代器上循环:for ch in text.chars()。不仅如此,而且你的索引循环和字符迭代器可能会分歧,因为 len() 返回的是字节数,而 chars() 返回的是 Unicode 标量值。作为 UTF-8,字符串可能具有比它具有字节更少的 Unicode 标量值。
下一个难题是 to_digit(10) 返回一个 Option,告诉你字符可能不是数字。你可以用 if let Some(digit) = ch.to_digit(10) 检查 to_digit(10) 是否返回了 OptionSome 变体。
把这些拼起来,代码现在可能看起来像这样:
fn main() {
    let text = "731671";
    let mut v = Vec::new();
    for ch in text.chars() {
        if let Some(digit) = ch.to_digit(10) {
            v.push(digit);
        }
    }
    println!("{:?}", v);
}

现在,这非常重要:你正在创建一个向量,并逐个数字填充它。你可以尝试一种更声明式或函数式的方法,通过对字符串应用转换来实现。
fn main() {
    let text = "731671";
    let v: Vec<u32> = text.chars().flat_map(|ch| ch.to_digit(10)).collect();
    println!("{:?}", v);
}

4

ArtemGr的回答非常好,但他们的版本会跳过任何不是数字的字符。如果您希望它在遇到错误的数字时失败,则可以使用此版本:

fn to_digits(text: &str) -> Option<Vec<u32>> {
    text.chars().map(|ch| ch.to_digit(10)).collect()
}

fn main() {
    println!("{:?}", to_digits("731671"));
    println!("{:?}", to_digits("731six71"));
}

输出:

Some([7, 3, 1, 6, 7, 1])
None

0

这个解决方案结合了ArtemGrnotriddle的解决方案:

fn to_digits(string: &str) -> Vec<u32> {
    let opt_vec: Option<Vec<u32>> = string
    .chars()
    .map(|ch| ch.to_digit(10))
    .collect();

    match opt_vec {
        Some(vec_of_digits) => vec_of_digits,
        None => vec![],
    }
}

在我的情况下,我在 &str 中实现了这个函数。
pub trait ExtraProperties {
    fn to_digits(self) -> Vec<u32>;
}
impl ExtraProperties for &str {
    fn to_digits(self) -> Vec<u32> {
        let opt_vec: Option<Vec<u32>> = self
        .chars()
        .map(|ch| ch.to_digit(10))
        .collect();

        match opt_vec {
            Some(vec_of_digits) => vec_of_digits,
            None => vec![],
        }
    }
}

通过这种方式,我将&str转换为一个包含数字的向量。

fn main() {
    let cnpj: &str = "123456789";
    let nums: Vec<u32> = cnpj.to_digits();
    
    println!("cnpj: {cnpj}");   // cnpj: 123456789
    println!("nums: {nums:?}"); // nums: [1, 2, 3, 4, 5, 6, 7, 8, 9]
}

请查看Rust Playground



0

提到房间里的快速而肮脏的大象,如果你确实知道你的字符串只包含范围在'0'..'9'之间的数字,那么你可以避免内存分配和复制,并直接使用str::as_bytes中的底层&[u8]表示形式。每当访问它时,从每个元素中减去b'0'

如果你正在进行竞争性编程,这是值得一试的速度和内存优化之一。

fn main() {
    let text = "12345";
    let digit = text.as_bytes();
    println!("Text = {:?}", text);
    println!("value of digit[3] = {}", digit[3] - b'0');
}

输出:

Text = "12345"
value of digit[3] = 4

在许多情况下,您可能会有一个String。如果是这样,您可以使用String::into_bytes,然后内联减去这些值。示例 - Shepmaster
不错的观点,如果您需要重复使用生成的字节数组中的所有值。 - Felix Köhler
但是转换的时间复杂度为O(n),而不是O(1)。因此,如果只需要一次访问并且只需要字符串的一部分,那么这种方式就不值得了。此外,您将失去对底层字符串的访问权,“这会消耗掉字符串...”,如果您正在编写一个跟踪索引并使用字符串切片来解析字符串不同组件的解析器,则需要该访问权。 - Felix Köhler

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