【问题标题】:How can I take input from either stdin or a file if I cannot seek stdin?如果我无法寻找标准输入,如何从标准输入或文件中获取输入?
【发布时间】:2016-05-14 07:07:34
【问题描述】:

我正在将一些 Python 移植到 Rust 作为学习练习,并且需要从文件或标准输入中获取输入。我在一个结构中保留了我的输入句柄,所以我想我只是创建一个Box<io::Read>,但我遇到了需要在输入中寻找的情况,而seek 不是Read 的一部分特征。我知道你不能在管道中寻找,所以我现在继续假设这个方法只在输入是一个文件时被调用,但我的问题是我不能在 Rust 中检查并向下转换。

我知道我可以为这两种输入类型使用枚举,但似乎应该有一种更优雅的方式来做到这一点。这就是我的问题,你如何做到这一点又不会弄得一团糟?

是否可以将标准输入或文件包装在同一种缓冲区中,这样我就可以使用那种类型而不必担心 IO 的类型?

【问题讨论】:

  • seek输入需要什么操作?如果你真的需要一个任意的seek,唯一的希望就是将整个标准输入读入Cursor<Vec<u8>>
  • 你显然不需要寻找你是否可以处理从标准输入读取。

标签: io rust traits downcast


【解决方案1】:

我知道,你说你想要一些更优雅且没有枚举的东西,但我认为枚举解决方案 相当优雅。所以这是一种尝试:

use std::fs;
use std::io::{self, Read, Seek, SeekFrom};

enum Input {
    File(fs::File),
    Stdin(io::Stdin),
}

impl Read for Input {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            Input::File(ref mut file) => file.read(buf),
            Input::Stdin(ref mut stdin) => stdin.read(buf),
        }
    }
}

impl Seek for Input {
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
        match *self {
            Input::File(ref mut file) => file.seek(pos),
            Input::Stdin(_) => {
                Err(io::Error::new(
                    io::ErrorKind::Other, 
                    "not supported by stdin-input",
                ))
            },
        }
    }
}

将这样的代码放在你的某个子模块中,不要再担心了。您可以使用Input 类型的对象,就像使用File 一样:无论如何您都必须处理查找错误,因此处理标准输入无法查找的问题应该非常容易。一个例子:

let arg = std::env::args().nth(1).unwrap();
let mut input = if arg == "--" {
    Input::Stdin(io::stdin())
} else {
    Input::File(fs::File::open(&arg).expect("I should handle that.."))
};

let mut v = Vec::new();
let _idc = input.read_to_end(&mut v);

match input.seek(SeekFrom::End(0)) {
    Err(_) => println!("oh noes :("),
    Ok(bytes) => println!("yeah, input is {} long", bytes),
}

【讨论】:

    【解决方案2】:

    是否可以将标准输入或文件包装在相同类型的缓冲区中,这样我就可以使用该类型而不必担心 io 的类型?

    这正是特征Read 的作用。似乎您想要的是 StdinFile 的抽象(特征),它具有对 seek 的可选支持,并允许查询此支持。在以下代码中,OptionalSeekRead trait 用于实现此目的:

    use std::io::{Read, Seek, SeekFrom, Stdin};
    use std::fs::File;
    
    // define a trait alias
    pub trait SeekRead: Seek + Read {}
    
    impl<T: Seek + Read> SeekRead for T {}
    
    pub trait OptionSeekRead: Read {
        fn get_seek_read(&mut self) -> Option<&mut SeekRead>;
    }
    
    impl OptionSeekRead for File {
        fn get_seek_read(&mut self) -> Option<&mut SeekRead> {
            Some(self)
        }
    }
    
    impl OptionSeekRead for Stdin {
        fn get_seek_read(&mut self) -> Option<&mut SeekRead> {
            None
        }
    }
    
    struct Handle {
        read: Box<OptionSeekRead>,
    }
    
    impl Handle {
        fn f(&mut self) {
            if let Some(h) = self.read.get_seek_read() {
                // h is Seek + Read
                h.seek(SeekFrom::Start(42));
            } else {
                // without Seek
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-04
      • 2020-01-09
      • 1970-01-01
      • 2012-11-05
      • 2011-03-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多