遍历一个字符串,一次 n 个元素

Iterate over a string, n elements at a time

我试图遍历一个字符串,但在长度 n 的切片中进行迭代,而不是遍历每个字符。以下代码手动完成此操作,但是否有更实用的方法来执行此操作?

fn main() {
    let string = "AAABBBCCC";
    let offset = 3;
    for (i, _) in string.chars().enumerate() {
        if i % offset == 0 {
            println!("{}", &string[i..(i+offset)]);
        }
    }
}

chunks() 不适用于 &str,因为它在字符串上的定义并不明确 - 您想要长度以字节、字符或字素簇为单位的块吗?如果您事先知道您的字符串是 ASCII 格式,您可以使用以下代码:

use std::str;

fn main() {
    let string = "AAABBBCCC";
    for chunk in str_chunks(string, 3) {
        println!("{}", chunk);
    }
}

fn str_chunks<'a>(s: &'a str, n: usize) -> Box<Iterator<Item=&'a str>+'a> {
    Box::new(s.as_bytes().chunks(n).map(|c| str::from_utf8(c).unwrap()))
}

但是,如果您的字符串中包含非 ASCII 字符,它会立即中断。我很确定可以实现一个将字符串拆分为代码点块或字素簇的迭代器 - 现在标准库中没有这样的东西。

您始终可以实现自己的迭代器。当然,这仍然需要相当多的代码,但它不在您使用字符串的位置。因此你的循环保持可读性。

#![feature(collections)]

struct StringChunks<'a> {
    s: &'a str,
    step: usize,
    n: usize,
}

impl<'a> StringChunks<'a> {
    fn new(s: &'a str, step: usize) -> StringChunks<'a> {
        StringChunks {
            s: s,
            step: step,
            n: s.chars().count(),
        }
    }
}

impl<'a> Iterator for StringChunks<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        if self.step > self.n {
            return None;
        }
        let ret = self.s.slice_chars(0, self.step);
        self.s = self.s.slice_chars(self.step, self.n);
        self.n -= self.step;
        Some(ret)
    }
}

fn main() {
    let string = "AAABBBCCC";
    for s in StringChunks::new(string, 3) {
        println!("{}", s);
    }
}

请注意,这会在 n 个 unicode 字符之后拆分。所以字素或类似物最终可能会分裂。

我会使用 PeekableTake 的组合:

fn main() {
    let string = "AAABBBCCC";
    let mut z = string.chars().peekable();
    while z.peek().is_some() {
        let chunk: String = z.by_ref().take(3).collect();
        println!("{}", chunk);
    }
}

在其他情况下,Itertools::chunks 可能会成功:

extern crate itertools;

use itertools::Itertools;

fn main() {
    let string = "AAABBBCCC";
    for chunk in &string.chars().chunks(3) {
        for c in chunk {
            print!("{}", c);
        }
        println!();
    }
}

关于拆分字符串的标准警告

每当您开始拆分字符串时,请注意字节/字符/代码点/字素的问题。对于任何比 ASCII 字符更复杂的字符,一个字符 不是 一个字节,字符串切片操作 字节 !还有 Unicode 代码点的概念,但多个 Unicode 字符可能组合起来形成人类认为的单个字符。这东西非平凡

如果您实际上只有 ASCII 数据,那么将其原样存储可能是值得的,也许在 Vec<u8> 中。至少,我会创建一个包装 &str 的新类型,并且只公开 ASCII 安全方法并在创建时验证它是 ASCII。