在同一个字符串上运行多个连续替换

mka*_*ito 7 rust

我找到了这个替换子字符串的例子:

use std::str;
let string = "orange";
let new_string = str::replace(string, "or", "str");
Run Code Online (Sandbox Code Playgroud)

如果我想在同一个字符串上运行多个连续替换,为了清理目的,如何在不为每个替换分配新变量的情况下执行此操作?

如果您要编写惯用的Rust,您将如何编写多个链式子串替换?

Tim*_*mmm 10

我不会为此使用正则表达式 or .replace().replace().replace()or 。.maybe_replace().maybe_replace().maybe_replace()他们都有很大的缺陷。

  • 正则表达式可能是最合理的选择,但如果您完全可以避免正则表达式,那么正则表达式只是一个可怕的想法。如果您的模式来自用户输入,那么您将不得不处理转义它们,这是一场安全噩梦。
  • .replace().replace().replace()由于显而易见的原因,这很糟糕。
  • .maybe_replace().maybe_replace().maybe_replace()仅比这稍好一些,因为它仅在模式不匹配时提高效率。如果它们全部匹配,它不会避免重复分配,在这种情况下,它实际上更糟糕,因为它会搜索字符串两次。

有一个更好的解决方案:使用Aho-Corasick crate。自述文件中甚至还有一个示例:

use aho_corasick::AhoCorasick;

let patterns = &["fox", "brown", "quick"];
let haystack = "The quick brown fox.";
let replace_with = &["sloth", "grey", "slow"];

let ac = AhoCorasick::new(patterns);
let result = ac.replace_all(haystack, replace_with);
assert_eq!(result, "The slow grey sloth.");
Run Code Online (Sandbox Code Playgroud)

用于消毒目的

我还应该说,将“坏”字符串列入黑名单是完全错误的清理方法。


She*_*ter 6

您将如何编写多个链接的子字符串替换?

我会按照要求去做:

fn main() {
    let a = "hello";
    let b = a.replace("e", "a").replace("ll", "r").replace("o", "d");
    println!("{}", b);
}
Run Code Online (Sandbox Code Playgroud)

如果您问的是如何进行多个并发替换,只通过一次字符串,那么它确实变得更加困难。

这确实需要为每次replace调用分配新的内存,即使不需要替换。的替代实现replace可能会返回 a Cow<str>,它仅在发生替换时包含拥有的变体。一个hacky实现可能看起来像:

use std::borrow::Cow;

trait MaybeReplaceExt<'a> {
    fn maybe_replace(self, needle: &str, replacement: &str) -> Cow<'a, str>;
}

impl<'a> MaybeReplaceExt<'a> for &'a str {
    fn maybe_replace(self, needle: &str, replacement: &str) -> Cow<'a, str> {
        // Assumes that searching twice is better than unconditionally allocating
        if self.contains(needle) {
            self.replace(needle, replacement).into()
        } else {
            self.into()
        }
    }
}

impl<'a> MaybeReplaceExt<'a> for Cow<'a, str> {
    fn maybe_replace(self, needle: &str, replacement: &str) -> Cow<'a, str> {
        // Assumes that searching twice is better than unconditionally allocating
        if self.contains(needle) {
            self.replace(needle, replacement).into()
        } else {
            self
        }
    }
}

fn main() {
    let a = "hello";
    let b = a.maybe_replace("e", "a")
        .maybe_replace("ll", "r")
        .maybe_replace("o", "d");
    println!("{}", b);

    let a = "hello";
    let b = a.maybe_replace("nope", "not here")
        .maybe_replace("still no", "i swear")
        .maybe_replace("but no", "allocation");
    println!("{}", b);
    assert_eq!(b.as_ptr(), a.as_ptr());
}
Run Code Online (Sandbox Code Playgroud)


win*_*ner 5

正则表达式引擎可以用来做字符串替换多个单传,但我会感到惊讶,如果这实际上是更好的性能:

extern crate regex;

use regex::{Captures, Regex};

fn main() {
    let re = Regex::new("(or|e)").unwrap();
    let string = "orange";
    let result = re.replace_all(string, |cap: &Captures| {
        match &cap[0] {
            "or" => "str",
            "e" => "er",
            _ => panic!("We should never get here"),
        }.to_string()
    });
    println!("{}", result);
}
Run Code Online (Sandbox Code Playgroud)

  • 从 regex crate 的 1.0.5 版开始,不需要 `match &amp;cap[0] { ... }.to_string()` 中的 `.to_string()`,因为 `Regex::replace_all` 闭包可以返回任何 `T: AsRef&lt;str&gt;`,而不仅仅是 `String`。所以它可以直接返回字符串文字(`&amp;'static str`)。 (2认同)