如何使用Rust从stdin创建一个高效的char迭代器?

最大的国家

既然Read::chars迭代器已被正式弃用,那么从Reader类似stdin的字符中获取迭代器而不将整个流读入内存的正确方法是什么

最大的国家

正如其他一些人提到的那样,可以复制不推荐使用的实现,Read::chars以供您自己的代码使用。这是否真正理想,取决于您的用例-对我来说,到目前为止,这已经足够好了,尽管我的应用程序在不久的将来可能会超过这种方法。

为了说明如何做到这一点,让我们看一个具体的例子:

use std::io::{self, Error, ErrorKind, Read};
use std::result;
use std::str;

struct MyReader<R> {
    inner: R,
}

impl<R: Read> MyReader<R> {
    fn new(inner: R) -> MyReader<R> {
        MyReader {
            inner,
        }
    }

#[derive(Debug)]
enum MyReaderError {
    NotUtf8,
    Other(Error),
}

impl<R: Read> Iterator for MyReader<R> {
    type Item = result::Result<char, MyReaderError>;

    fn next(&mut self) -> Option<result::Result<char, MyReaderError>> {
        let first_byte = match read_one_byte(&mut self.inner)? {
            Ok(b) => b,
            Err(e) => return Some(Err(MyReaderError::Other(e))),
        };
        let width = utf8_char_width(first_byte);
        if width == 1 {
            return Some(Ok(first_byte as char));
        }
        if width == 0 {
            return Some(Err(MyReaderError::NotUtf8));
        }
        let mut buf = [first_byte, 0, 0, 0];
        {
            let mut start = 1;
            while start < width {
                match self.inner.read(&mut buf[start..width]) {
                    Ok(0) => return Some(Err(MyReaderError::NotUtf8)),
                    Ok(n) => start += n,
                    Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
                    Err(e) => return Some(Err(MyReaderError::Other(e))),
                }
            }
        }
        Some(match str::from_utf8(&buf[..width]).ok() {
            Some(s) => Ok(s.chars().next().unwrap());
            None => Err(MyReaderError::NotUtf8),
        })
    }
}

上面的代码也需要read_one_byte并且utf8_char_width将要实现。这些应该看起来像:

static UTF8_CHAR_WIDTH: [u8; 256] = [
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF
0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF
4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
];

fn utf8_char_width(b: u8) -> usize {
    return UTF8_CHAR_WIDTH[b as usize] as usize;
}

fn read_one_byte(reader: &mut Read) -> Option<io::Result<u8>> {
    let mut buf = [0];
    loop {
        return match reader.read(&mut buf) {
            Ok(0) => None,
            Ok(..) => Some(Ok(buf[0])),
            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
            Err(e) => Some(Err(e)),
        };
    }
}

现在,我们可以使用MyReader实现char在某些阅读器上生成的迭代器,例如io::stdin::Stdin

fn main() {
    let stdin = io::stdin();
    let mut reader = MyReader::new(stdin.lock());
    for c in reader {
        println!("{}", c);
    }
}

原始发行线程中详细讨论了此方法的局限性但是,需要特别指出的一个问题是,此迭代器将无法正确处理非UTF-8编码的流。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

在Rust中,如何创建可变的迭代器?

迭代一个char位

如何编写一个带迭代器的Rust函数?

Univocity-如何使用迭代器样式每行返回一个bean?

C ++如何在结构向量的一个字段上创建迭代器

Nim:如何从另一个迭代器包装/派生一个迭代器?

如何创建一个从stdin读取的Scala XMLEventReader?

如何在python中创建一个迭代器类,它将以相反的顺序迭代输入?

如何使用另一个实例列表在Java中高效创建实例列表

定义一个采用迭代器而不使用Rust的函数的惯用方式是什么?

创建一个堆栈和使用for循环在堆栈上运行的迭代器

Rust函数需要一个迭代器,并返回带有变异项的迭代器?

如何在Rust中定义一个包含可迭代项的结构上的迭代器?

在Ranges-v3中,如何从一对迭代器创建一个范围?

如何创建一个迭代器,该迭代器生成由Rust中的索引列表指定的集合元素?

函数式编程,如何从一个迭代器一次高效地构建多个列表?

如何在Rust中为引用创建一个“可迭代”特征?

创建一个惰性哈希迭代器

在Rust中多次使用同一个迭代器

如何使用一对迭代器(第一个,最后一个)在模板中创建一个容器?

创建一个反向迭代器的函数

创建一个蛮力迭代器

如何创建一个输入是对象或可迭代的集合

如何创建迭代器重复 Rust 中的最后一个元素

如何使用 Eigen 在矩阵块上构造一个方便的迭代器

如何创建一个调用成员函数的类似 stl 的迭代器

Rust 在 env::Args 上使用迭代器方法会产生错误,也许是一个错误?

如何创建一个带有迭代器的 Rust 结构?

如何创建一个带有迭代器的结构?