有没有办法可以直接从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?
}
Run Code Online (Sandbox Code Playgroud)
我如何直接config
从文件中读取配置?这甚至可能吗?
She*_*ter 12
正如Vladimir Matveev 提到的,使用byteorder crate通常是最好的解决方案。这样,您就可以解决字节序问题,不必处理任何不安全的代码,也不必担心对齐或填充:
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?
}
Run Code Online (Sandbox Code Playgroud)
我忽略了以下[char; 8]
几个原因:
char
是 32 位类型,不清楚您的文件是否具有实际的 Unicode 代码点或 C 风格的 8 位值。注意:针对稳定的Rust更新(目前为1.10).
干得好:
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);
}
Run Code Online (Sandbox Code Playgroud)
但是,您需要小心,因为不安全的代码是不安全的.特别是在slice::from_raw_parts_mut()
调用之后,同时存在两个对同一数据的可变句柄,这违反了Rust别名规则.因此,您希望在最短的时间内保持从结构中创建的可变切片.我还假设你知道有关字节序的问题 - 上面的代码绝不是可移植的,如果在不同类型的机器上编译和运行(例如ARM vs x86),它将返回不同的结果.
如果您可以选择格式并且想要一个紧凑的二进制格式,请考虑使用bincode.否则,如果您需要解析一些预定义的二进制结构,那么可以使用byteorder crate.
请注意,以下代码未考虑任何字节顺序或填充问题,并且旨在与POD类型一起使用.struct Configuration
在这种情况下应该是安全的.
这是一个可以从文件中读取(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)
Run Code Online (Sandbox Code Playgroud)
如果要从文件中读取一系列结构,可以read_struct
多次执行或一次读取所有文件:
use std::fs::{self, File};
use std::io::BufReader;
use std::path::Path;
fn read_structs<T, P: AsRef<Path>>(path: P) -> io::Result<Vec<T>> {
let path = path.as_ref();
let struct_size = ::std::mem::size_of::<T>();
let num_bytes = fs::metadata(path)?.len() as usize;
let num_structs = num_bytes / struct_size;
let mut reader = BufReader::new(File::open(path)?);
let mut r = Vec::<T>::with_capacity(num_structs);
unsafe {
let buffer = slice::from_raw_parts_mut(r.as_mut_ptr() as *mut u8, num_bytes);
reader.read_exact(buffer)?;
r.set_len(num_structs);
}
Ok(r)
}
// use
// read_structs::<StructName, _>("path/to/file"))
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
8023 次 |
最近记录: |