如果无法查找标准输入,我如何从标准输入或文件获取输入?
How can I take input from either stdin or a file if I cannot seek stdin?
我正在将一些 Python 移植到 Rust 作为学习练习,需要从文件或标准输入中获取输入。我在一个结构中保留了我的输入句柄,所以我想我只是做一个 Box<io::Read>
但我 运行 进入了我需要寻找输入的情况,而 seek
不是' Read
特征的一部分。我知道你不能在管道中查找,所以我现在继续假设只有当输入是文件时才会调用此方法,但我的问题是我无法检查它并在 Rust 中向下转换。
我知道我可以对两种输入类型使用枚举,但似乎应该有更优雅的方法来做到这一点。这就是我的问题,你怎么做才不会弄得一团糟?
是否可以将 stdin 或文件包装在同一种缓冲区中,这样我就可以只使用该类型而不用担心 IO 的类型?
Is it possible to wrap stdin or a file in the same sort of buffer so I could just use that type and not worry about the type of io?
这正是特征 Read
所做的。似乎您想要的是 Stdin
和 File
的抽象(特征),它具有对 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
}
}
}
我知道,您说过您想要更优雅且没有枚举的东西,但我认为枚举解决方案 相当优雅。所以这是一种尝试:
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",
))
},
}
}
}
把这样的代码放在你的一些子模块中,不要再担心太多了。您可以像使用 File
一样使用 Input
类型的对象:无论如何您都必须处理查找错误,因此处理无法通过标准输入查找应该非常容易。一个例子:
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),
}
我正在将一些 Python 移植到 Rust 作为学习练习,需要从文件或标准输入中获取输入。我在一个结构中保留了我的输入句柄,所以我想我只是做一个 Box<io::Read>
但我 运行 进入了我需要寻找输入的情况,而 seek
不是' Read
特征的一部分。我知道你不能在管道中查找,所以我现在继续假设只有当输入是文件时才会调用此方法,但我的问题是我无法检查它并在 Rust 中向下转换。
我知道我可以对两种输入类型使用枚举,但似乎应该有更优雅的方法来做到这一点。这就是我的问题,你怎么做才不会弄得一团糟?
是否可以将 stdin 或文件包装在同一种缓冲区中,这样我就可以只使用该类型而不用担心 IO 的类型?
Is it possible to wrap stdin or a file in the same sort of buffer so I could just use that type and not worry about the type of io?
这正是特征 Read
所做的。似乎您想要的是 Stdin
和 File
的抽象(特征),它具有对 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
}
}
}
我知道,您说过您想要更优雅且没有枚举的东西,但我认为枚举解决方案 相当优雅。所以这是一种尝试:
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",
))
},
}
}
}
把这样的代码放在你的一些子模块中,不要再担心太多了。您可以像使用 File
一样使用 Input
类型的对象:无论如何您都必须处理查找错误,因此处理无法通过标准输入查找应该非常容易。一个例子:
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),
}