Rust HDF5文件操作库hdf5-sys的使用:高性能科学数据存储与读写接口
Rust HDF5文件操作库hdf5-sys的使用:高性能科学数据存储与读写接口
hdf5-sys
crate提供了对HDF5 C库的直接绑定,并允许根据需要从C源代码构建库,以便静态链接。
静态链接
这个crate支持链接到HDF5的静态构建版本。HDF5 C库是通过hdf5-src
crate构建的,当设置了static
功能时会被链接进来。下面是静态构建支持的选项列表。
截至本文撰写时,构建的HDF5库版本是1.10.5,但后续可能会升级。
Crate功能
功能会根据所选库的可用功能自动检测。选择的库可以通过指定环境变量HDF5_DIR
来覆盖。
通用功能:
mpio
: 启用MPI支持(静态模式下不支持)static
: 从源代码构建HDF5 C库(见下面的选项列表)
这些选项是互斥的。这种组合未来可能会支持。
以下功能会影响从源代码编译HDF5时的构建选项:
hl
: 启用高级功能(我们没有提供绑定)threadsafe
: 构建线程安全版本的库zlib
: 启用zlib
过滤器支持deprecated
: 包含已弃用的符号(我们没有提供绑定)
注意:HDF5库有单独的BSD-style license。
安装
在项目目录中运行以下Cargo命令:
cargo add hdf5-sys
或者在Cargo.toml中添加以下行:
hdf5-sys = "0.8.1"
完整示例代码
下面是一个使用hdf5-sys创建HDF5文件并写入/读取数据的完整示例:
use hdf5_sys::h5f::{H5Fcreate, H5Fclose, H5F_ACC_TRUNC};
use hdf5_sys::h5t::{H5T_NATIVE_INT, H5Tcopy, H5Tclose};
use hdf5_sys::h5s::{H5Screate_simple, H5Sclose, H5S_ALL};
use hdf5_sys::h5d::{H5Dcreate2, H5Dwrite, H5Dread, H5Dclose};
use hdf5_sys::h5p::{H5P_DEFAULT};
fn main() -> Result<(), Box<dyn std::error::Error>> {
unsafe {
// 创建新HDF5文件
let file_name = "test.h5".as_ptr() as *const i8;
let file_id = H5Fcreate(file_name, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
// 创建数据类型(32位整数)
let type_id = H5Tcopy(H5T_NATIVE_INT);
// 创建数据空间(1维数组,10个元素)
let dims = [10];
let space_id = H5Screate_simple(1, dims.as_ptr(), std::ptr::null());
// 创建数据集
let dataset_name = "my_data".as_ptr() as *const i8;
let dataset_id = H5Dcreate2(
file_id,
dataset_name,
type_id,
space_id,
H5P_DEFAULT,
H5P_DEFAULT,
H5P_DEFAULT
);
// 写入数据
let write_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
H5Dwrite(
dataset_id,
H5T_NATIVE_INT,
H5S_ALL,
H5S_ALL,
H5P_DEFAULT,
write_data.as_ptr() as *const std::ffi::c_void
);
// 读取数据
let mut read_data = [0; 10];
H5Dread(
dataset_id,
H5T_NATIVE_INT,
H5S_ALL,
H5S_ALL,
H5P_DEFAULT,
read_data.as_mut_ptr() as *mut std::ffi::c_void
);
println!("读取的数据: {:?}", read_data);
// 关闭所有资源
H5Dclose(dataset_id);
H5Sclose(space_id);
H5Tclose(type_id);
H5Fclose(file_id);
}
Ok(())
}
这个示例展示了如何:
- 创建一个新的HDF5文件
- 定义数据类型和数据空间
- 创建数据集并写入数据
- 从数据集中读取数据
- 关闭所有HDF5资源
注意:在实际应用中,你需要添加错误处理代码来检查每个HDF5操作的返回值。
Rust HDF5文件操作库hdf5-sys的使用:高性能科学数据存储与读写接口
介绍
hdf5-sys是Rust语言中用于操作HDF5文件的底层绑定库,它提供了对HDF5 C API的直接访问。HDF5(Hierarchical Data Format)是一种用于存储和组织大量科学数据的文件格式,广泛应用于科学计算、工程仿真和数据分析领域。
hdf5-sys是更高级的hdf5库的基础,适合需要直接控制HDF5底层功能或构建自定义HDF5接口的开发人员使用。
使用方法
添加依赖
首先在Cargo.toml中添加依赖:
[dependencies]
hdf5-sys = "0.10"
基本示例
use hdf5_sys::h5::*;
use std::ffi::CString;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化HDF5库
unsafe { H5open()?; }
// 创建新文件
let file_name = CString::new("example.h5")?;
let file_id = unsafe { H5Fcreate(file_name.as_ptr(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT) }?;
// 创建数据集
let dims = [10, 10];
let space_id = unsafe { H极Screate_simple(2, dims.as_ptr(), std::ptr::null()) }?;
let dataset_name = CString::new("dataset")?;
let dataset_id = unsafe {
H5Dcreate2(
file_id,
dataset_name.as_ptr(),
H5T_STD_I32LE,
space_id,
H5P_DEFAULT,
H5P_DEFAULT,
H5P_DEFAULT,
)
}?;
// 写入数据
let data: Vec<i32> = (0..100).collect();
unsafe { H5Dwrite(dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data.as_ptr() as *const _)?; }
// 关闭资源
unsafe {
H5Dclose(dataset_id)?;
H5Sclose(space_id)?;
H5Fclose(file_id)?;
}
Ok(())
}
读取HDF5文件
use hdf5_sys::h5::*;
use std::ffi::CString;
fn read_hdf5() -> Result极(), Box<dyn std::error::Error>> {
let file_name = CString::new("example.h5")?;
let file_id = unsafe { H5Fopen(file_name.as_ptr(), H5F_ACC_RDONLY, H5P_DEFAULT) }?;
let dataset_name = CString::new("dataset")?;
let dataset_id = unsafe { H5Dopen2(file_id, dataset_name.as_ptr(), H5P_DEFAULT) }?;
let space_id = unsafe { H5Dget_space(dataset_id) }?;
let mut dims = [0; 2];
unsafe { H5Sget_simple_extent_dims(space_id, dims.as_mut_ptr(), std::ptr::null_mut())?; }
let total_elements = dims[0] * dims[1];
let mut data = vec![0; total_elements as usize];
unsafe {
H5Dread(
dataset_id,
H5T_NATIVE_INT,
H5S_ALL,
H5S_ALL,
H5P_DEFAULT,
data.as_mut_ptr() as *mut _,
)?;
}
println!("Read data: {:?}", data);
unsafe {
H5Sclose(space_id)?;
H5Dclose(dataset_id)?;
H5Fclose(file_id)?;
}
Ok(())
}
创建和读取属性
use hdf5_sys::h5::*;
use std::ffi::CString;
fn attributes_example() -> Result<(), Box<dyn std::error::Error>> {
let file_name = CString::new("attributes.h5")?;
let file_id = unsafe { H5Fcreate(file_name.as_ptr(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT) }?;
// 创建根组的属性
let attr_name = CString::new("version")?;
let attr_value = 1.0f64;
let space_id = unsafe { H5Screate(H5S_SCALAR) }?;
let attr_id = unsafe {
H5Acreate2(
file_id,
attr_name.as_ptr(),
H5T_IEEE_F64LE,
space_id,
H5P_DEFAULT,
H5极_DEFAULT,
)
}?;
unsafe {
H5Awrite(attr_id, H5T_NATIVE_DOUBLE, &attr_value as *const _ as *const _)?;
H5Aclose(attr_id)?;
H5Sclose(space_id)?;
}
// 读取属性
let attr_id = unsafe { H5Aopen(file_id, attr_name.as_ptr(), H5P_DEFAULT) }?;
let mut read_value = 0.0f64;
unsafe {
H5Aread(attr_id, H5T_NATIVE_DOUBLE, &mut read_value as *mut _ as *mut _)?;
H5Aclose(attr_id)?;
}
println!("Attribute value: {}", read_value);
unsafe { H5Fclose(file_id)?; }
Ok(())
}
高级功能
使用压缩过滤器
use hdf5_sys::h5::*;
use std::ffi::CString;
fn create_compressed_dataset() -> Result<(), Box<dyn std::error::Error>> {
let file_name = CString::new("compressed.h5")?;
let file_id = unsafe { H5Fcreate(file_name.as_ptr(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT) }?;
// 创建数据集创建属性列表
let plist_id = unsafe { H5Pcreate(H5P_DATASET_CREATE) }?;
unsafe {
// 启用压缩
H5Pset_deflate(plist_id, 6)?;
// 设置分块
let chunk_dims = [100, 100];
H5Pset_chunk(plist_id, 2, chunk_dims.as_ptr())?;
}
// 创建数据集
let dims = [1000, 1000];
let space_id = unsafe { H5Screate_simple(2, dims.as_ptr(), std::ptr::null()) }?;
let dataset_name = CString::new("compressed_data")?;
let dataset_id = unsafe {
H5Dcreate2(
file_id,
dataset_name.as_ptr(),
H5T_STD_I32LE,
space_id,
H5P_DEFAULT,
plist_id,
H5P_DEFAULT,
)
}?;
// 写入数据
let total_elements = dims[0] * dims[1];
let data = vec![42; total_elements as usize];
unsafe {
H5Dwrite(dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data.as_ptr() as *const _)?;
H5Dclose(dataset_id)?;
H5Sclose(space_id)?;
H5Pclose(plist_id)?;
H5Fclose(file_id)?;
}
Ok(())
}
注意事项
- hdf5-sys是底层绑定,使用时需要处理大量的unsafe代码
- 确保正确关闭所有打开的HDF5对象以避免资源泄漏
- 错误处理需要检查每个HDF5调用的返回值
- 对于大多数应用场景,推荐使用更高级的hdf5库而非直接使用hdf5-sys
- 需要系统安装HDF5库,可通过包管理器安装或从源码编译
性能建议
- 对于大型数据集,使用分块存储和压缩
- 批量写入数据比多次小量写入更高效
- 选择合适的数据类型减少存储空间和I/O时间
- 考虑使用集体操作(collective operations)进行并行I/O
完整示例
以下是一个完整的HDF5文件读写示例,演示了如何创建文件、写入数据集、添加属性以及读取数据:
use hdf5_sys::h5::*;
use std::ffi::CString;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化HDF5库
unsafe { H5open()?; }
// 1. 创建新文件并写入数据
create_and_write_hdf5()?;
// 2. 读取HDF5文件
read_hdf5_file()?;
// 3. 属性操作示例
handle_attributes()?;
// 4. 压缩数据集示例
create_compressed_dataset()?;
Ok(())
}
fn create_and_write_hdf5() -> Result<(), Box<dyn std::error::Error>> {
// 创建新文件
let file_name = CString::new("data.h5")?;
let file_id = unsafe { H5Fcreate(file_name.as_ptr(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT) }?;
// 创建2D数据集
let dims = [100, 50];
let space_id = unsafe { H5Screate_simple(2, dims.as_ptr(), std::ptr::null()) }?;
let dataset_name = CString::new("temperature")?;
let dataset_id = unsafe {
H5Dcreate2(
file_id,
dataset_name.as_ptr(),
H5T_STD_F32LE,
space_id,
H5P_DEFAULT,
H5P_DEFAULT,
H5P_DEFAULT,
)
}?;
// 生成并写入数据
let mut data = vec![0.0f32; dims[0] * dims[1]];
for i in 0..dims[0] {
for j in 0..dims[1] {
data[i * dims[1] + j] = (i + j) as f32 * 0.5;
}
}
unsafe {
H5Dwrite(dataset_id, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data.as_ptr() as *const _)?;
}
// 关闭资源
unsafe {
H5Dclose(dataset_id)?;
H5Sclose(space_id)?;
H5Fclose(file_id)?;
}
println!("成功创建并写入HDF5文件");
Ok(())
}
fn read_hdf5_file() -> Result<(), Box<dyn std::error::Error>> {
let file_name = CString::new("data.h5")?;
let file_id = unsafe { H5Fopen(file_name.as_ptr(), H5F_ACC_RDONLY, H5P_DEFAULT) }?;
let dataset_name = CString::new("temperature")?;
let dataset_id = unsafe { H5Dopen2(file_id, dataset_name.as_ptr(), H5P_DEFAULT) }?;
// 获取数据集维度
let space_id = unsafe { H5Dget_space(dataset_id) }?;
let mut dims = [0; 2];
unsafe { H5Sget_simple_extent_dims(space_id, dims.as_mut_ptr(), std::ptr::null_mut())?; }
// 读取数据
let total_elements = dims[0] * dims[1];
let mut data = vec![0.0f32; total_elements as usize];
unsafe {
H5Dread(
dataset_id,
H5T_NATIVE_FLOAT,
H5S_ALL,
H5S_ALL,
H5P_DEFAULT,
data.as_mut_ptr() as *mut _,
)?;
}
// 打印部分数据
println!("数据集维度: {:?}", dims);
println!("前10个数据点: {:?}", &data[..10]);
unsafe {
H5Sclose(space_id)?;
H5Dclose(dataset_id)?;
H5Fclose(file_id)?;
}
Ok(())
}
fn handle_attributes() -> Result<(), Box<dyn std::error::Error>> {
let file_name = CString::new("data.h5")?;
let file_id = unsafe { H5Fopen(file_name.as_ptr(), H5F_ACC_RDWR, H5P_DEFAULT) }?;
// 创建属性
let attr_name = CString::new("creation_date")?;
let attr_value = CString::new("2023-11-15")?;
let space_id = unsafe { H5Screate(H5S_SCALAR) }?;
let type_id = unsafe { H5Tcopy(H5T_C_S1)? };
unsafe { H5Tset_size(type_id, attr_value.as_bytes().len() as _)? };
let attr_id = unsafe {
H5Acreate2(
file_id,
attr_name.as_ptr(),
type_id,
space_id,
H5P_DEFAULT,
H5P_DEFAULT,
)
}?;
unsafe {
H5Awrite(attr_id, type_id, attr_value.as_ptr() as *const _)?;
H5Aclose(attr_id)?;
H5Sclose(space_id)?;
H5Tclose(type_id)?;
}
// 读取属性
let attr_id = unsafe { H5Aopen(file_id, attr_name.as_ptr(), H5P_DEFAULT) }?;
let mut buf = vec![0u8; 20];
unsafe {
H5Aread(attr_id, type_id, buf.as_mut_ptr() as *mut _)?;
H5Aclose(attr_id)?;
}
let value = String::from_utf8(buf)?.trim_end_matches('\0').to_string();
println!("属性值: {}", value);
unsafe { H5Fclose(file_id)?; }
Ok(())
}
fn create_compressed_dataset() -> Result<(), Box<dyn std::error::Error>> {
let file_name = CString::new("compressed.h5")?;
let file_id = unsafe { H5Fcreate(file_name.as_ptr(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT) }?;
// 创建带压缩的数据集
let plist_id = unsafe { H5Pcreate(H5极_DATASET_CREATE) }?;
unsafe {
H5Pset_deflate(plist_id, 6)?;
let chunk_dims = [100, 100];
H5Pset_chunk(plist_id, 2, chunk_dims.as_ptr())?;
}
let dims = [1000, 500];
let space_id = unsafe { H5Screate_simple(2, dims.as_ptr(), std::ptr::null()) }?;
let dataset_name = CString::new("compressed_data")?;
let dataset_id = unsafe {
H5Dcreate2(
file_id,
dataset_name.as_ptr(),
H5T_STD_I32LE,
space_id,
H5P_DEFAULT,
plist_id,
H5P_DEFAULT,
)
}?;
// 写入数据
let total_elements = dims[0] * dims[1];
let data = vec![42; total_elements as usize];
unsafe {
H5Dwrite(dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data.as_ptr() as *const _)?;
}
println!("成功创建压缩数据集");
unsafe {
H5Dclose(dataset_id)?;
H5Sclose(space_id)?;
H5Pclose(plist_id)?;
H5Fclose(file_id)?;
}
Ok(())
}
这个完整示例演示了:
- 创建HDF5文件并写入2D浮点数据集
- 读取HDF5文件并检查数据
- 添加和读取字符串属性
- 创建带压缩的分块数据集
每个函数都包含完整的错误处理和资源清理代码。