如何在 Rust 中将字符串拆分为块以插入空格
Posted
技术标签:
【中文标题】如何在 Rust 中将字符串拆分为块以插入空格【英文标题】:How to split string into chunks in Rust to insert spaces 【发布时间】:2019-11-23 13:54:35 【问题描述】:我正在尝试学习 Rust。我最近遇到的一个问题是:
给定一个String
,正好是 n 的某个倍数,我想将字符串拆分为大小为 n 的块,并在这些块之间插入一个空格,然后收集回单个字符串。
我遇到的问题是chars()
方法返回Chars
结构,由于某种原因它没有实现SliceConcatExt
特征,因此不能在其上调用chunks()
。
此外,一旦我成功创建了一个 Chunks 结构(通过调用 .bytes()
而不是),我不确定如何调用 .join(' ')
,因为元素现在是字节切片的 Chunks
...
必须有一种优雅的方式来做到这一点,我错过了。
例如,这里有一个说明情况的输入/输出:
given: whatupmyname, 4
output: what upmy name
这是我写得不好的尝试:
let n = 4;
let text = "whatupmyname".into_string();
text.chars()
// compiler error on chunks() call
.chunks(n)
.collect::<Vec<String>>()
.join(' ')
感谢您的帮助!
【问题讨论】:
我不知道你所说的字符串的“大小”是什么。您可以将é
(一个代码点,两个字节)分成多少块? e̊
(两个代码点,三个字节)呢? ????????
(两个代码点,八个字节)呢?
Creating a sliding window iterator of slices of chars from a String的可能重复
@trentcl 很公平,我想我应该指定在这种情况下我只担心 char 的 - 可以由这 128 个字节表示的字符。哪个更有限,但对于我的目的来说足够简单。
@hellow 这确实非常接近在字符串上创建滑动窗口,但我认为这种情况会有所不同,因为我试图创建块,然后将这些块收集到字符串中。我遇到了双方的问题:一旦将String转换为Vec这里的问题是chars()
和bytes()
返回Iterator
s,而不是切片。你可以使用as_bytes()
,它会给你一个&[u8]
。但是,您不能直接从&str
中获取&[char]
,因为只存在字节本身,并且必须通过查看每个字节组成的字节数来创建char
s。你必须这样做:
text.chars()
.collect::<Vec<char>>()
.chunks(n)
.map(|c| c.iter().collect::<String>())
.collect::<Vec<String>>()
.join(" ");
但是,我不建议这样做,因为它必须为Vec
s 和String
s 分配大量临时存储空间。相反,你可以做这样的事情,它只需要分配来创建最终的String
。
text.chars()
.enumerate()
.flat_map(|(i, c)|
if i != 0 && i % n == 0
Some(' ')
else
None
.into_iter()
.chain(std::iter::once(c))
)
.collect::<String>()
在最后一次收集之前,它一直作为迭代器,通过 flat_mapping 与一个迭代器进行映射,该迭代器要么是字符,要么是空格,然后是字符。
【讨论】:
我在发布之前尝试了第一个建议(虽然我对制作不必要的 Vec 感到不安)但我在collect::<Vec<String>>()
调用时遇到了编译器错误。有句话说不能从Iterator<&[char]>
构建Vec<String>
,这让我觉得很奇怪。编辑:现在运行它,它指出 FromIterator<&[char]>
的特征没有为 Vec<String>
实现,所以我认为也许我可以实现该特征?
已修复。我不建议使用该代码,但它必须分配太多。顺便说一句,您将无法实现该特征,因为所涉及的特征和类型都不是“您的”。
哦,我明白了。我认为有一种方法可以将字符块隐式转换为字符串,但地图会为您做到这一点。此外,flat_map 概念对我来说有点陌生,但我将尝试解构它: - flat_map 通常会展平嵌套结构,但在这种情况下,它用于返回迭代器 - 如果你在第 n 个字符上,插入一个包含空格的迭代器,并将其链接到当前迭代器中,使其位于它之前。否则, None 将变成一个不会产生任何结果的迭代器。 - 将迭代器收集到一个字符串中【参考方案2】:
因此,如果您想从一个字符列表中创建一个字符串,您可以使用fold。
类似这样的:
text.chars
.enumerate()
.fold(String::new(), |acc, (i, c)|
if i != 0 && i == n
format!(" ", acc, c)
else
format!("", acc, c)
)
【讨论】:
哦,有趣,我也喜欢这个解决方案,感谢您花时间做出回应。这是一个非常清晰易懂的解决方案,我应该考虑过。谢谢!我很好奇与 JayDepp 发布的 flat_map 解决方案相比是否存在字符串分配开销。 所以,不幸的是,有。format!
正在创建一个 String
并返回它。因此,对于每个字符,都会创建一个新的String
,其中包含带有当前字符的前一个字符和一个可选的空格(如果需要)。最后,您将得到相同的String
,但由于有多个中间String
,因此会有相当多的开销。上述方法更好,因为您将拥有一个Iterator<Iterator<Char>>
,而flat_map
将创建一个Iterator<Char>
,在最后准备好成为collect
ed。所以你只会创建一个最终的String
。【参考方案3】:
如果要拆分的数据大小是固定的,那么:
use std::str;
fn main()
let subs = "‌​‌​‌​​‌​‌".as_bytes()
.chunks(7)
.map(str::from_utf8)
.collect::<Result<Vec<&str>, _>>()
.unwrap();
println!(":?", subs);
// >> ["‌", "​", "‌", "​", "‌", "​", "​", "‌", "​", "‌"]
【讨论】:
以上是关于如何在 Rust 中将字符串拆分为块以插入空格的主要内容,如果未能解决你的问题,请参考以下文章