我试图迭代一个字符串,但是在每个字符上迭代长度为n
的片而不是迭代器。以下代码手动完成此操作,但有更实用的方法吗?
fn main() {
let string = "AAABBBCCC";
let offset = 3;
for (i, _) in string.chars().enumerate() {
if i % offset == 0 {
println!("{}", &string[i..(i+offset)]);
}
}
}
答案 0 :(得分:9)
我会使用Peekable
和Take
的组合:
fn main() {
let string = "AAABBBCCC";
let mut z = string.chars().peekable();
while z.peek().is_some() {
let chunk: String = z.by_ref().take(3).collect();
println!("{}", chunk);
}
}
在其他情况下,Itertools::chunks
可能会解决问题:
extern crate itertools;
use itertools::Itertools;
fn main() {
let string = "AAABBBCCC";
for chunk in &string.chars().chunks(3) {
for c in chunk {
print!("{}", c);
}
println!();
}
}
每当开始拆分字符串时,请注意字节/字符/代码点/字形的问题。使用比ASCII字符更复杂的字符,一个字符不一个字节,字符串切片操作字节!还有Unicode代码点的概念,但是多个Unicode字符可以组合形成人类认为的单个字符。这个东西非平凡。
如果您实际上只有ASCII数据,那么将其存储为Vec<u8>
可能是值得的。至少,我创建了一个包装&str
的新类型,并且只显示ASCII安全方法,并在创建时验证它是ASCII。
答案 1 :(得分:2)
chunks()
不适用于&str
,因为它在字符串上的定义不是很明确 - 您是否希望块长度以字节为单位,或字符或字形集群?如果您事先知道您的字符串是ASCII格式,则可以使用以下代码:
use std::str;
fn main() {
let string = "AAABBBCCC";
for chunk in str_chunks(string, 3) {
println!("{}", chunk);
}
}
fn str_chunks<'a>(s: &'a str, n: usize) -> Box<Iterator<Item=&'a str>+'a> {
Box::new(s.as_bytes().chunks(n).map(|c| str::from_utf8(c).unwrap()))
}
但是,如果您的字符串中包含非ASCII字符,它将立即中断。我非常确定可以实现一个迭代器,它将一个字符串拆分成代码点或字形集块 - 现在标准库中没有这样的东西了。
答案 2 :(得分:2)
您始终可以实现自己的迭代器。当然,这仍然需要相当多的代码,但它不在您使用字符串的位置。因此,你的循环保持可读性。
#![feature(collections)]
struct StringChunks<'a> {
s: &'a str,
step: usize,
n: usize,
}
impl<'a> StringChunks<'a> {
fn new(s: &'a str, step: usize) -> StringChunks<'a> {
StringChunks {
s: s,
step: step,
n: s.chars().count(),
}
}
}
impl<'a> Iterator for StringChunks<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
if self.step > self.n {
return None;
}
let ret = self.s.slice_chars(0, self.step);
self.s = self.s.slice_chars(self.step, self.n);
self.n -= self.step;
Some(ret)
}
}
fn main() {
let string = "AAABBBCCC";
for s in StringChunks::new(string, 3) {
println!("{}", s);
}
}
请注意,这会在n
unicode字符之后拆分。因此,字形或类似物可能最终分裂。