如何查找并替换每个匹配的字节切片为另一个切片?

9

我有一个字节向量,想要用[4, 5, 6]替换每个[1, 2, 3]。在Rust中如何实现?

let mut buf = vec![1, 2, 3, 7, 8];

// ?

assert_eq!(buf, vec![4, 5, 6, 7, 8]);

1
替换的切片长度一定与要替换的切片相同吗?或者你需要缩小/扩大 Vec 吗? - trent
1
我认为目前没有任何方便的通用例程定义在&[u8]上,可以像str::replace方法一样为您完成此操作。我可能会自己实现它,可能通过复制str::replace的实现方式来实现(https://doc.rust-lang.org/src/alloc/str.rs.html#268-278)。 (我正在为Rust编写一个字节字符串库,肯定会支持此类操作。但它还没有完成。) - BurntSushi5
将链接的问题应用于此处建议使用 buf[0..3].copy_from_slice(&[4, 5, 6]);。如果切片大小不同,则可能是 Efficiently insert or replace multiple elements in the middle or at the beginning of a Vec? 的重复。 - trent
另一个问题:如果你在[1,2,3,3]中用[5,1,2]取代[1,2,3],你希望得到的结果是[5,1,2,3]吗?两个现有的答案都会给你[5,5,1,2] - trent
显示剩余5条评论
3个回答

2

当切片的长度不同时,此方法有效(类似于strreplace方法):

fn replace<T>(source: &[T], from: &[T], to: &[T]) -> Vec<T>
where
    T: Clone + PartialEq
{
    let mut result = source.to_vec();
    let from_len = from.len();
    let to_len = to.len();

    let mut i = 0;
    while i + from_len <= result.len() {
        if result[i..].starts_with(from) {
            result.splice(i..i + from_len, to.iter().cloned());
            i += to_len;
        } else {
            i += 1;
        }
    }

    result
}

2
这个函数可以完成这项工作:
fn replace_slice<T>(source: &mut [T], from: &[T], to: &[T])
where
    T: Clone + PartialEq,
{
    let iteration = if source.starts_with(from) {
        source[..from.len()].clone_from_slice(to);
        from.len()
    } else {
        1
    };

    if source.len() > from.len() {
        replace_slice(&mut source[iteration..], from, to);
    }
}

这个函数是递归的,但你也可以使用循环重写它。

示例1:

fn main() {
    let mut buf = vec![1, 2, 3, 7, 8, 1, 2, 3];

    replace_slice(&mut buf[..], &[1, 2, 3], &[4, 5, 6]);

    assert_eq!(buf, vec![4, 5, 6, 7, 8, 4, 5, 6]);
}

游乐场


示例2: (来自评论者trentcl)

fn main() {
    let mut buf = vec![1, 2, 3, 3, 4, 1, 2, 3];

    replace_slice(&mut buf[..], &[1, 2, 3], &[5, 1, 2]);

    assert_eq!(buf, vec![5, 1, 2, 3, 4, 5, 1, 2]);
}

Playground


这个实现使用了递归,所以当向量的元素超过100000个时,会抛出“致命运行时错误:堆栈溢出”。 - undefined
@umitu 递归已经在答案中指出,堆栈溢出在某一点之后并不令人意外,可以用迭代实现相同的思路。 - undefined

0

不使用递归:

fn replace_slice<T>(buf: &mut [T], from: &[T], to: &[T])
where
    T: Clone + PartialEq,
{
    for i in 0..=buf.len() - from.len() {
        if buf[i..].starts_with(from) {
            buf[i..(i + from.len())].clone_from_slice(to);
        }
    }
}

1
你可以考虑将循环改为 for i in 0..=buf.len() - from.len(),以跳过不必要的检查。 - Ömer Erden
@ÖmerErden 当 from 大于 source 时,source.len() - from.len() 将会引发 panic。 - trent
@trentcl 感谢您指出这个问题,当“from”和“to”大小不相等时,由于“starts_with”中的断言,它也会引发panic。在循环之前添加保护将有所帮助。 - Ömer Erden
@ÖmerErden,如果 selfneedle 的大小不同,starts_with 不会引发 panic;它只会返回 false - trent
@trentcl 抱歉,应该是“clone_from_slice”。 - Ömer Erden

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