遍历一个字符串,一次 n 个元素
Iterate over a string, n elements at a time
我试图遍历一个字符串,但在长度 n
的切片中进行迭代,而不是遍历每个字符。以下代码手动完成此操作,但是否有更实用的方法来执行此操作?
fn main() {
let string = "AAABBBCCC";
let offset = 3;
for (i, _) in string.chars().enumerate() {
if i % offset == 0 {
println!("{}", &string[i..(i+offset)]);
}
}
}
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 字符,它会立即中断。我很确定可以实现一个将字符串拆分为代码点块或字素簇的迭代器 - 现在标准库中没有这样的东西。
您始终可以实现自己的迭代器。当然,这仍然需要相当多的代码,但它不在您使用字符串的位置。因此你的循环保持可读性。
#![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 字符之后拆分。所以字素或类似物最终可能会分裂。
我会使用 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。
我试图遍历一个字符串,但在长度 n
的切片中进行迭代,而不是遍历每个字符。以下代码手动完成此操作,但是否有更实用的方法来执行此操作?
fn main() {
let string = "AAABBBCCC";
let offset = 3;
for (i, _) in string.chars().enumerate() {
if i % offset == 0 {
println!("{}", &string[i..(i+offset)]);
}
}
}
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 字符,它会立即中断。我很确定可以实现一个将字符串拆分为代码点块或字素簇的迭代器 - 现在标准库中没有这样的东西。
您始终可以实现自己的迭代器。当然,这仍然需要相当多的代码,但它不在您使用字符串的位置。因此你的循环保持可读性。
#![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 字符之后拆分。所以字素或类似物最终可能会分裂。
我会使用 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。