如何使用 Rust 从 stdin 创建高效的字符迭代器?

2024-01-23

现在既然Read::chars https://doc.rust-lang.org/nightly/std/io/trait.Read.html#method.chars迭代器已被正式弃用 https://github.com/rust-lang/rust/issues/27802#issuecomment-383940293,获取来自 a 的字符的迭代器的正确方法是什么Reader就像标准输入一样,而不将整个流读入内存?


弃用的相应问题 https://github.com/rust-lang/rust/issues/27802#issuecomment-377537778很好地总结了问题Read::chars并提出建议:

不关心增量处理数据的代码可以使用Read::read_to_string反而。大概也关心的代码 想要控制其缓冲策略并与&[u8] and &str尽可能大的切片,而不是一片char在 一次。它应该基于str::from_utf8功能以及 这valid_up_to and error_len的方法Utf8Error https://doc.rust-lang.org/nightly/std/str/struct.Utf8Error.html类型。一个棘手的方面是处理单个char是 用 UTF-8 表示的多个字节,这些字节恰好是 分成不同的read调用/缓冲块。 (Utf8Error::error_len返回None表明这可能是 案子。)The utf-8 crate https://crates.io/crates/utf-8解决 这个,但为了灵活,提供了一个 API,可能有 标准库中包含的表面太多。

当然,以上是针对始终为 UTF-8 的数据。如果是其他 需要支持字符编码,请考虑使用encoding_rs https://crates.io/crates/encoding_rs or encoding https://crates.io/crates/encoding crate.

你自己的迭代器

The 最有效率的解决方案I/O 调用次数是将所有内容读入一个巨大的缓冲区String并迭代:

use std::io::{self, Read};

fn main() {
    let stdin = io::stdin();
    let mut s = String::new();
    stdin.lock().read_to_string(&mut s).expect("Couldn't read");
    for c in s.chars() {
        println!(">{}<", c);
    }
}

您可以将其与答案结合起来String::chars 是否有自有版本? https://stackoverflow.com/q/47193584/155423:

use std::io::{self, Read};

fn reader_chars<R: Read>(mut rdr: R) -> io::Result<impl Iterator<Item = char>> {
    let mut s = String::new();
    rdr.read_to_string(&mut s)?;
    Ok(s.into_chars()) // from https://stackoverflow.com/q/47193584/155423
}

fn main() -> io::Result<()> {
    let stdin = io::stdin();

    for c in reader_chars(stdin.lock())? {
        println!(">{}<", c);
    }

    Ok(())
}

我们现在有一个返回迭代器的函数chars 对于任何实现的类型Read.

一旦有了这种模式,只需决定在哪里进行内存分配与 I/O 请求的权衡即可。这是使用行大小缓冲区的类似想法:

use std::io::{BufRead, BufReader, Read};

fn reader_chars<R: Read>(rdr: R) -> impl Iterator<Item = char> {
    // We use 6 bytes here to force emoji to be segmented for demo purposes
    // Pick more appropriate size for your case
    let reader = BufReader::with_capacity(6, rdr);

    reader
        .lines()
        .flat_map(|l| l) // Ignoring any errors
        .flat_map(|s| s.into_chars())  // from https://stackoverflow.com/q/47193584/155423
}

fn main() {
    // emoji are 4 bytes each
    let data = "????????????????";
    let data = data.as_bytes();

    for c in reader_chars(data) {
        println!(">{}<", c);
    }
}

最极端的情况是为每个字符执行一个 I/O 请求。这不会占用太多内存,但会产生大量 I/O 开销。

务实的答案

复制并粘贴执行Read::chars到你自己的代码中。它将像以前一样工作。

也可以看看:

  • String::chars 是否有自有版本? https://stackoverflow.com/q/47193584/155423
  • 如何按字符迭代字符串 https://stackoverflow.com/q/22118221/155423
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Rust 从 stdin 创建高效的字符迭代器? 的相关文章

随机推荐