使用Rust从不同偏移量的文件阅读

jchrr9hc  于 2023-02-08  发布在  其他
关注(0)|答案(3)|浏览(228)

我正在处理一个项目,该项目涉及从文件中以不同的偏移量阅读不同的信息。
目前,我使用的是以下代码:

// ------------------------ SECTORS PER CLUSTER ------------------------

// starts at 13
opened_file.seek(SeekFrom::Start(13)).unwrap();
let aux: &mut [u8] = &mut [0; 1];
let _buf = opened_file.read_exact(aux);

// ------------------------ RESERVED SECTORS ------------------------

// starts at 14
opened_file.seek(SeekFrom::Start(14)).unwrap();
let aux: &mut [u8] = &mut [0; 2];
let _buf = opened_file.read_exact(aux);

但是正如您所看到的,我需要创建一个新的缓冲区,其大小是我每次都要读取的,我不能直接将其指定为函数的参数。
我试着创建一个结构体,但是我无法创建一个包含所有不同数据的结构体。例如:

struct FileStruct {
        a1: &mut [u8] &mut [0; 1],
        a2: &mut [u8] &mut [0; 2],
    }

read_exact方法工作需要哪些类型?
有没有一种更有效的方法可以从文件的不同偏移读取信息,而不必为我想从文件中读取的每一条信息重复复制粘贴这些代码行?有没有某种函数/光标/向量可以轻松地在偏移周围移动?有没有一种方法可以将这些信息写入结构域?

2wnc66cl

2wnc66cl1#

最简单的方法是拥有一个拥有数组的结构体,然后查找并读入该结构体。

use std::io::{self, prelude::*, SeekFrom};

#[derive(Debug, Clone, Default)]
struct FileStruct {
    a1: [u8; 1],
    a2: [u8; 2],
}

fn main() -> io::Result<()> {
    let mut file_struct: FileStruct = Default::default();
    let mut opened_file = unimplemented!(); // open file somehow
    opened_file.seek(SeekFrom::Start(13))?;
    opened_file.read_exact(&mut file_struct.a1)?;
    opened_file.seek(SeekFrom::Start(14))?;
    opened_file.read_exact(&mut file_struct.a2)?;
    println!("{:?}", file_struct);
    Ok(())
}

Playground link
这仍然是相当重复的,所以可以创建一个seek_read函数来减少重复:

use std::io::{self, prelude::*, SeekFrom};

#[derive(Debug, Clone, Default)]
struct FileStruct {
    a1: [u8; 1],
    a2: [u8; 2],
}

fn seek_read(mut reader: impl Read + Seek, offset: u64, buf: &mut [u8]) -> io::Result<()> {
    reader.seek(SeekFrom::Start(offset))?;
    reader.read_exact(buf)?;
    Ok(())
}

fn main() -> io::Result<()> {
    let mut file_struct: FileStruct = Default::default();
    let mut opened_file = unimplemented!(); // open file somehow
    seek_read(&mut opened_file, 13, &mut file_struct.a1)?;
    seek_read(&mut opened_file, 14, &mut file_struct.a2)?;
    println!("{:?}", file_struct);
    Ok(())
}

Playground link
使用宏可以进一步降低重复次数:

use std::io::{self, prelude::*, SeekFrom};

#[derive(Debug, Clone, Default)]
struct FileStruct {
    a1: [u8; 1],
    a2: [u8; 2],
}

macro_rules! read_offsets {
    ($file: ident, $file_struct: ident, []) => {};
    ($file: ident, $file_struct: ident, [$offset: expr => $field: ident $(, $offsets: expr => $fields: ident)*]) => {
        $file.seek(SeekFrom::Start($offset))?;
        $file.read_exact(&mut $file_struct.$field)?;
        read_offsets!($file, $file_struct, [$($offsets => $fields),*]);
    }
}

fn main() -> io::Result<()> {
    let mut file_struct: FileStruct = Default::default();
    let mut opened_file = unimplemented!(); // open file somehow
    read_offsets!(opened_file, file_struct, [13 => a1, 14 => a2]);
    println!("{:?}", file_struct);
    Ok(())
}

Playground link

mwngjboj

mwngjboj2#

这是对Aplet 123的补充回答:不太清楚你 * 必须 * 将字节原样存储到一个结构中,所以你也可以分配一个缓冲区(作为一个固定大小的数组),并在大小正确的切片中重用它。

let mut buf = [0u8;16];
opened_file.read_exact(&mut buf[..4])?; // will read 4 bytes
// do thing with the first 4 bytes
opened_file.read_exact(&mut buf[..8])?; // will read 8 bytes this time
// etc...

你也可以使用the byteorder crate,它可以让你使用directly read numbers or sequences of numbers,它基本上只做不依赖的“创建适当大小的堆栈缓冲区;阅读;解码”。
这一点特别有用,因为它看起来很像“每个群集的扇区”应该是u8,“保留扇区”应该是u16。使用byteorder,您可以直接使用read_16()read_u8()

ffx8fchx

ffx8fchx3#

同样基于Aplet123的答案,下面的函数seek_read不需要知道在编译时要读取多少字节,因为它使用Vector而不是字节片:

// Starting at `offset`, reads the `amount_to_read` from `reader`.
// Returns the bytes as a vector.
fn seek_read(
    reader: &mut (impl Read + Seek),
    offset: u64,
    amount_to_read: usize,
) -> Result<Vec<u8>> {
    // A buffer filled with as many zeros as we'll read with read_exact
    let mut buf = vec![0; amount_to_read];
    reader.seek(SeekFrom::Start(offset))?;
    reader.read_exact(&mut buf)?;
    Ok(buf)
}

以下是一些测试,用于演示seek_read的行为:

use std::io::Cursor;
#[test]
fn seek_read_works() {
    let bytes = b"Hello world!";
    let mut reader = Cursor::new(bytes);

    assert_eq!(seek_read(&mut reader, 0, 2).unwrap(), b"He");
    assert_eq!(seek_read(&mut reader, 1, 4).unwrap(), b"ello");
    assert_eq!(seek_read(&mut reader, 6, 5).unwrap(), b"world");
    assert_eq!(seek_read(&mut reader, 2, 0).unwrap(), b"");
}

#[test]
#[should_panic(expected = "failed to fill whole buffer")]
fn seek_read_beyond_buffer_fails() {
    let mut reader = Cursor::new(b"Hello world!");
    seek_read(&mut reader, 6, 99).unwrap();
}

#[test]
#[should_panic(expected = "failed to fill whole buffer")]
fn start_seek_reading_beyond_buffer_fails() {
    let mut reader = Cursor::new(b"Hello world!");
    seek_read(&mut reader, 99, 1).unwrap();
}

相关问题