如何在Rust中从字符串开头“裁剪”字符?

6
我希望有一个函数,它可以接受两个参数 (字符串,需要裁剪掉前面的字母数),并返回同样的字符串,除了在字符x之前的字母已经被删除。

如果我写下:

let mut example = "stringofletters";
CropLetters(example, 3);
println!("{}", example);

然后输出应该为:

ingofletters

有没有办法我可以做到这一点?

4个回答

10
在许多情况下,仅返回输入的切片是有意义的,避免任何复制。将@Shepmaster的解决方案转换为使用不可变切片:
fn crop_letters(s: &str, pos: usize) -> &str {
    match s.char_indices().skip(pos).next() {
        Some((pos, _)) => &s[pos..],
        None => "",
    }
}

fn main() {
    let example = "stringofletters"; // works with a String if you take a reference
    let cropped = crop_letters(example, 3);
    println!("{}", cropped);
}

与可变版本相比的优点有:

  • 无需复制。如果您想要新分配的结果,可以调用 cropped.to_string();但您不必这样做。
  • 它适用于静态字符串片段以及可变的 String 等。

缺点是,如果您真的有一个想要修改的可变字符串,它会稍微不那么高效,因为您需要分配一个新的 String


6

您原来的代码存在以下问题:

  1. 函数使用 snake_case,类型和特质使用 CamelCase
  2. "foo" 是一个类型为 &str 的字符串字面量。它们不能被更改。您需要使用已经在堆上分配的内容,比如 String
  3. 调用 crop_letters(stringofletters, 3) 将会 转移所有权 给该方法,这意味着您将无法再使用该变量。您必须传递可变引用 (&mut)。
  4. Rust 字符串不是 ASCII,而是UTF-8。您需要弄清每个字符需要多少个字节。在这里,char_indices 是一个好工具。
  5. 您需要处理字符串长度小于 3 的情况。
  6. 一旦您获得了新字符串开头的字节位置,就可以使用 drain 将一块字节移出字符串。我们只是丢弃这些字节,并让 String 移动剩余的字节。
fn crop_letters(s: &mut String, pos: usize) {
    match s.char_indices().nth(pos) {
        Some((pos, _)) => {
            s.drain(..pos);
        }
        None => {
            s.clear();
        }
    }
}

fn main() {
    let mut example = String::from("stringofletters");
    crop_letters(&mut example, 3);
    assert_eq!("ingofletters", example);
}

如果您实际上不需要修改原始的String,请参见Chris Emerson的答案

0

我发现这个答案并不是我认为的非常地道:

fn crop_with_allocation(string: &str, len: usize) -> String {
    string.chars().skip(len).collect()
}

fn crop_without_allocation(string: &str, len: usize) -> &str {
    // optional length check
    if string.len() < len {
        return &"";
    }
    &string[len..]
}

fn main() {
    let example = "stringofletters"; // works with a String if you take a reference
    let cropped = crop_with_allocation(example, 3);
    println!("{}", cropped);
    let cropped = crop_without_allocation(example, 3);
    println!("{}", cropped);
}

这个解决方案的“问题”在于它假设输入字符串要么是ASCII,要么用户已经找到了正确的字节偏移量。例如,对于输入字符串“åáâä”,此方法将失败。 - Shepmaster

0

我的版本

fn crop_str(s: &str, n: usize) -> &str {
    let mut it = s.chars();
    for _ in 0..n {
        it.next();
    }
    it.as_str()
}

#[test]
fn test_crop_str() {
    assert_eq!(crop_str("123", 1), "23");
    assert_eq!(crop_str("ЖФ1", 1), "Ф1");
    assert_eq!(crop_str("ЖФ1", 2), "1");
}

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