从String创建一个chars切片的滑动窗口迭代器

bma*_*tin 6 string iterator utf-8 slice rust

我正在寻找最好的方法StringWindows<T>使用windows切片提供的功能.

我理解如何以这种方式使用Windows:

fn main() {
    let tst = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
    let mut windows = tst.windows(3);

    // prints ['a', 'b', 'c']
    println!("{:?}", windows.next().unwrap());
    // prints ['b', 'c', 'd']
    println!("{:?}", windows.next().unwrap());
    // etc...
}
Run Code Online (Sandbox Code Playgroud)

但是在处理这个问题时我有点迷失:

fn main() {
    let tst = String::from("abcdefg");
    let inter = ? //somehow create slice of character from tst
    let mut windows = inter.windows(3);

    // prints ['a', 'b', 'c']
    println!("{:?}", windows.next().unwrap());
    // prints ['b', 'c', 'd']
    println!("{:?}", windows.next().unwrap());
    // etc...
}
Run Code Online (Sandbox Code Playgroud)

本质上,我正在寻找如何将字符串转换为我可以使用窗口方法的char切片.

hel*_*low 8

此解决方案将适用于您的目的.(游乐场)

fn main() {
    let tst = String::from("abcdefg");
    let inter = tst.chars().collect::<Vec<char>>();
    let mut windows = inter.windows(3);

    // prints ['a', 'b', 'c']
    println!("{:?}", windows.next().unwrap());
    // prints ['b', 'c', 'd']
    println!("{:?}", windows.next().unwrap());
    // etc...
    println!("{:?}", windows.next().unwrap());
}
Run Code Online (Sandbox Code Playgroud)

字符串可以遍历其字符,但它不是切片,因此您必须将其收集到vec中,然后vec强制转换为切片.


Pet*_*all 7

你面临的问题是,String它实际上表现为类似于底层的东西Vec<u8>,有一些API可以让你访问chars.在UTF-8中,代码点的表示可以是1到4个字节的任何值,并且为了节省空间,它们都被压缩在一起.

String没有复制所有内容的情况下,你可以直接得到一个整体的唯一切片就是a &[u8],但是你不知道这些字节是对应于整个代码点还是只是代码点的一部分.

char类型完全对应于代码点,因此其大小为4个字节,因此它可以容纳任何可能的值.因此,如果char通过从a复制来构建一个片段String,结果可能会增加4倍.

为了避免进行潜在的大型临时内存分配,您应该考虑一种更懒惰的方法 - 迭代String,使切片完全在char边界处.像这样的东西:

fn char_windows<'a>(src: &'a str, win_size: usize) -> impl Iterator<Item = &'a str> {
    src.char_indices()
        .flat_map(move |(from, _)| {
            src[from ..].char_indices()
                .skip(win_size - 1)
                .next()
                .map(|(to, c)| {
                    &src[from .. from + to + c.len_utf8()]
                })
    })
}
Run Code Online (Sandbox Code Playgroud)

这将为您提供一个迭代器,其中的项目均为&str3 char秒:

let mut windows = char_windows(&tst, 3);

for win in windows {
    println!("{:?}", win);
}
Run Code Online (Sandbox Code Playgroud)

这种方法的好处在于它根本没有完成任何复制 - 每个&str迭代器产生的仍然是原始源的切片String.


所有这些复杂性都是因为Rust默认情况下对字符串使用UTF-8编码.如果您完全知道输入字符串不包含任何多字节字符,则可以将其视为ASCII字节,并且可以轻松获取切片:

let tst = String::from("abcdefg");
let inter = tst.as_bytes();
let mut windows = inter.windows(3);
Run Code Online (Sandbox Code Playgroud)

但是,您现在有了一些字节片段,并且您需要将它们转换回字符串以对它们执行任何操作:

for win in windows {
    println!("{:?}", String::from_utf8_lossy(win));
}
Run Code Online (Sandbox Code Playgroud)

  • 这是对幕后发生的事情的精彩解释 - 感谢您对它的揭示! (2认同)

She*_*ter 7

您可以使用itertools遍历任何迭代器的窗口,宽度最大为4:

extern crate itertools; // 0.7.8

use itertools::Itertools;

fn main() {
    let input = "???";

    for (a, b) in input.chars().tuple_windows() {
        println!("{}, {}", a, b);
    }
}
Run Code Online (Sandbox Code Playgroud)

也可以看看: