Io 如何从Rust中的文件读取结构?

Io 如何从Rust中的文件读取结构?,io,rust,Io,Rust,有没有一种方法可以直接从Rust中的文件读取结构?我的代码是: use std::fs::File; struct Configuration { item1: u8, item2: u16, item3: i32, item4: [char; 8], } fn main() { let file = File::open("config_file").unwrap(); let mut config: Configuration;

有没有一种方法可以直接从Rust中的文件读取结构?我的代码是:

use std::fs::File;

struct Configuration {
    item1: u8,
    item2: u16,
    item3: i32,
    item4: [char; 8],
}

fn main() {
    let file = File::open("config_file").unwrap();

    let mut config: Configuration;
    // How to read struct from file?
}
如何从文件中将配置直接读入
config
?这可能吗

给你:

use std::io::Read;
use std::mem;
use std::slice;

#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
struct Configuration {
    item1: u8,
    item2: u16,
    item3: i32,
    item4: [char; 8],
}

const CONFIG_DATA: &[u8] = &[
    0xfd, // u8
    0xb4, 0x50, // u16
    0x45, 0xcd, 0x3c, 0x15, // i32
    0x71, 0x3c, 0x87, 0xff, // char
    0xe8, 0x5d, 0x20, 0xe7, // char
    0x5f, 0x38, 0x05, 0x4a, // char
    0xc4, 0x58, 0x8f, 0xdc, // char
    0x67, 0x1d, 0xb4, 0x64, // char
    0xf2, 0xc5, 0x2c, 0x15, // char
    0xd8, 0x9a, 0xae, 0x23, // char
    0x7d, 0xce, 0x4b, 0xeb, // char
];

fn main() {
    let mut buffer = CONFIG_DATA;

    let mut config: Configuration = unsafe { mem::zeroed() };

    let config_size = mem::size_of::<Configuration>();
    unsafe {
        let config_slice = slice::from_raw_parts_mut(&mut config as *mut _ as *mut u8, config_size);
        // `read_exact()` comes from `Read` impl for `&[u8]`
        buffer.read_exact(config_slice).unwrap();
    }

    println!("Read structure: {:#?}", config);
}
使用std::io::Read;
使用std::mem;
使用std::slice;
#[报告员(C、C)]
#[派生(调试、复制、克隆)]
结构配置{
第1项:u8,
项目2:u16,
项目3:i32,
第4项:[字符;8],
}
常量配置数据:&[u8]=&[
0xfd,//u8
0xb4,0x50,//u16
0x45,0xcd,0x3c,0x15,//i32
0x71、0x3c、0x87、0xff、//字符
0xe8、0x5d、0x20、0xe7、//字符
0x5f、0x38、0x05、0x4a、//字符
0xc4、0x58、0x8f、0xdc、//字符
0x67,0x1d,0xb4,0x64,//字符
0xf2、0xc5、0x2c、0x15、//字符
0xd8、0x9a、0xae、0x23、//字符
0x7d、0xce、0x4b、0xeb、//字符
];
fn main(){
让mut buffer=CONFIG_DATA;
让mut-config:Configuration=unsafe{mem::zeroed()};
让config_size=mem::size_of::();
不安全{
让config_slice=slice::from_raw_parts_mut(&mut config as*mut u as*mut u8,config_size);
//'read_exact()'来自'read'impl for`&[u8]`
buffer.read_exact(config_slice).unwrap();
}
println!(“读取结构:{:#?}”,配置);
}
(更新了锈1.38)

然而,您需要小心,因为不安全的代码是不安全的。调用
slice::from_raw\u parts_mut()
后,同一数据同时存在两个可变句柄,这违反了锈迹别名规则。因此,您可能希望在尽可能短的时间内保持在结构之外创建的可变切片。我还假设您知道endianness问题——上面的代码绝不是可移植的,如果在不同类型的机器上编译和运行(例如ARM和x86),将返回不同的结果


<>如果你可以选择格式,你需要一个紧凑的二进制格式,考虑使用。否则,如果您需要(例如)解析某些预定义的二进制结构,则可以使用crate。

以下代码不考虑任何或任何问题,旨在与一起使用<代码>结构配置在这种情况下应该是安全的


下面是一个可以从文件中读取结构(POD类型)的函数:

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

fn read_struct<T, R: Read>(mut read: R) -> io::Result<T> {
    let num_bytes = ::std::mem::size_of::<T>();
    unsafe {
        let mut s = ::std::mem::uninitialized();
        let buffer = slice::from_raw_parts_mut(&mut s as *mut T as *mut u8, num_bytes);
        match read.read_exact(buffer) {
            Ok(()) => Ok(s),
            Err(e) => {
                ::std::mem::forget(s);
                Err(e)
            }
        }
    }
}

// use
// read_struct::<Configuration>(reader)
因为,使用通常是最好的解决方案。通过这种方式,您可以解决端点问题,不必处理任何不安全的代码,也不必担心对齐或填充:

use byteorder::{LittleEndian, ReadBytesExt}; // 1.2.7
use std::{
    fs::File,
    io::{self, Read},
};

struct Configuration {
    item1: u8,
    item2: u16,
    item3: i32,
}

impl Configuration {
    fn from_reader(mut rdr: impl Read) -> io::Result<Self> {
        let item1 = rdr.read_u8()?;
        let item2 = rdr.read_u16::<LittleEndian>()?;
        let item3 = rdr.read_i32::<LittleEndian>()?;

        Ok(Configuration {
            item1,
            item2,
            item3,
        })
    }
}

fn main() {
    let file = File::open("/dev/random").unwrap();

    let config = Configuration::from_reader(file);
    // How to read struct from file?
}
使用字节顺序::{LittleEndian,ReadBytesExt};//1.2.7
使用std::{
fs::文件,
io::{self,Read},
};
结构配置{
第1项:u8,
项目2:u16,
项目3:i32,
}
impl配置{
fn来自_读取器(mut-rdr:impl-Read)->io::Result{
设item1=rdr.read_u8()?;
设item2=rdr.read_u16::()?;
设item3=rdr.read_i32::()?;
Ok(配置{
项目1,
项目2,
项目3,
})
}
}
fn main(){
让file=file::open(“/dev/random”).unwrap();
让config=Configuration::from_reader(文件);
//如何从文件中读取结构?
}
出于以下几个原因,我忽略了
[char;8]

  • Rust的
    char
    是32位类型,不清楚您的文件是否具有实际的Unicode码点或C样式的8位值
  • 你不能轻易地用字节顺序解析数组,你必须解析N个值,然后自己构建数组

  • 你的文件有什么格式?正确答案很大程度上取决于文件中的实际数据表示形式。@VladimirMatveev二进制格式,我不想从文件中读取并复制到我的结构中;我想用我的结构作为缓冲区来读取文件。啊,我现在明白你需要什么了。如果没有一些不安全的代码,你就做不到。我现在试着写概念证明。这个板条箱似乎正是你想要的:是的,我知道endian的问题-但它只是我正在写的一个快速工具,可以在大约3台计算机上运行。@a.B.,我相信。它现在已经定位了。我使用了'mem::uninitialized',而不是最后的
    mem::zeroed
    。如果内存仍将被覆盖,那么将其初始化为0似乎没有多大意义。这会给我一条“警告,此警告将成为错误”消息,虽然此代码的总体轮廓很好,但此特定实例违反了Rust的安全性。字符数据的值无效,并且超出了当前支持的字符边界。为什么::std::mem。。。而不是std::mem?有什么区别吗?以
    开头的路径是绝对路径。如果将函数放在模块上,使用绝对路径将确保代码能够编译。搜索absolute in以了解更多信息。谢谢Malbarbow为什么这里需要a::std::mem::forget?这不是表示内存泄漏吗?@Knight用于防止析构函数在
    s
    上运行(
    s
    未初始化)。这是文档中描述的一个用例。我想这些
    read_u8
    和其他
    read_X
    调用可能会调用系统调用。所以它可能不是很有效。我们能以某种尾数而不是整数类型的小部分来读取整个结构吗?@VictorPolevoy这是缓冲读取器要修复的工作。请参阅,从“缓冲I/O”开始。但是是的,您可以不安全地获取任何随机的字节块,并将其转换为任何给定的类型。这就是这里另外两个答案的重点。如果我想读取10GB文件怎么办?性能惩罚将很高。使用原始零件是IMO的唯一方法。@mishmashru我不明白为什么它的性能会比原始零件的性能低。这不需要你有什么意见。编写这两个代码并对其进行基准测试,这样您就可以确定了。
    use byteorder::{LittleEndian, ReadBytesExt}; // 1.2.7
    use std::{
        fs::File,
        io::{self, Read},
    };
    
    struct Configuration {
        item1: u8,
        item2: u16,
        item3: i32,
    }
    
    impl Configuration {
        fn from_reader(mut rdr: impl Read) -> io::Result<Self> {
            let item1 = rdr.read_u8()?;
            let item2 = rdr.read_u16::<LittleEndian>()?;
            let item3 = rdr.read_i32::<LittleEndian>()?;
    
            Ok(Configuration {
                item1,
                item2,
                item3,
            })
        }
    }
    
    fn main() {
        let file = File::open("/dev/random").unwrap();
    
        let config = Configuration::from_reader(file);
        // How to read struct from file?
    }