将字符串拆分为字符处的 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,
}
}
}
}
使用它来实现您既定目标的示例:
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 .
假设我想在每个 '
处将一个像 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,
}
}
}
}
使用它来实现您既定目标的示例:
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 .