将字符串拆分为字符处的 Vec<String> 的最佳方法,当前面没有其他字符而不在 Rust 中环顾四周时?

best way to split string into Vec<String> at character, when not preceeded by other character without lookaround in Rust?

假设我想在每个 ' 处将一个像 abc'xyz?'zzz' 这样的字符串拆分成一个 Vec,但如果字符前面有一个 ?,则不会。 我想在没有 Regex 环顾四周的情况下实现这一点,因为我不相信输入。

我可以假设,输入是 UTF8 兼容的。

在 Rust 中实现这一点的最快(并且可能是最有效的内存方式)是什么?

我想遍历字符串并将子字符串保存到 var 中,如果下一个 Char 是 ',但通过 Char 比较,当前 Char 不是 ?。然后我会通过移动将该 var 的值推入 Vec 中。

这是个好主意,还是有更有效(时间和内存方面)的方法来实现?

实现此目的最惯用方法是将其变成Iterator的实现,采用&str并生成&str .

这是一个示例实现,假设输入字符串上的尾随 ' 应该 而不是 在它之后产生一个空元素,并且空字符串也不应该产生任何元素。请注意,没有创建任何副本,因为我们只是在处理字符串切片。如果您想生成 Vec<String>,那么您可以通过将迭代器映射到 str::to_owned 来实现。 (.map(str::to_owned).collect::<Vec<_>>())

use std::str::CharIndices;

// A verbose name for an oddly specific concept.
struct SplitStringAtCharNotFollowingCharIterator<'a> {
    delimiter: char,
    exception: char,
    text: &'a str,
    chars: CharIndices<'a>,
}

impl<'a> SplitStringAtCharNotFollowingCharIterator<'a> {
    pub fn new(text: &'a str, delimiter: char, exception: char) -> Self {
        Self { delimiter, exception, text, chars: text.char_indices() }
    }
}

impl<'a> Iterator for SplitStringAtCharNotFollowingCharIterator<'a> {
    type Item = &'a str;
    
    fn next(&mut self) -> Option<&'a str> {
        let first = self.chars.next();
        
        let (start, mut prior) = match first {
            None => return None,
            Some((_, c)) if c == self.delimiter => return Some(""),
            Some(v) => v,
        };
        
        loop {
            prior = match self.chars.next() {
                None => return Some(&self.text[start..]),
                
                Some((end, c)) if c == self.delimiter && prior != self.exception =>
                    return Some(&self.text[start..end]),
                
                Some((_, c)) => c,
            }
        }
    }
}

(Playground)

使用它来实现您既定目标的示例:

let pieces = SplitStringAtCharNotFollowingCharIterator::new("abc'xyz?'zzz'", '\'', '?')
    .map(str::to_owned)
    .collect::<Vec<_>>();

但是如果您实际上不需要拥有的字符串或向量,您可以直接使用迭代器,它不需要任何额外的堆分配,因为它分配原始切片的子切片。

我认为您不需要 over-complicate 这个 - 一个简单的 for 循环就可以了。 这也使您可以轻松准确地调整拆分的工作方式, 例如include/exclude 分隔符,如何处理空匹配。 Playground

fn split(s: &str) -> Vec<String> {
    let mut chunks = Vec::new();
    let mut cur = String::new();
    let mut last_char = None;
    for c in s.chars() {
        if c == '\'' && last_char != Some('?') {
            chunks.push(std::mem::take(&mut cur));
        } else {
            cur.push(c);
        }
        last_char = Some(c);
    }
    chunks.push(cur);
    chunks
}

如果你想生成 Vec<&str>,你需要做更多的工作来维护对现有字符串的引用,但由于我们要返回 Vec<String>,我们可以简单地复制字符 one-by-one .