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(())
}

这个示例展示了如何:

  1. 创建一个新的HDF5文件
  2. 定义数据类型和数据空间
  3. 创建数据集并写入数据
  4. 从数据集中读取数据
  5. 关闭所有HDF5资源

注意:在实际应用中,你需要添加错误处理代码来检查每个HDF5操作的返回值。


1 回复

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(())
}

注意事项

  1. hdf5-sys是底层绑定,使用时需要处理大量的unsafe代码
  2. 确保正确关闭所有打开的HDF5对象以避免资源泄漏
  3. 错误处理需要检查每个HDF5调用的返回值
  4. 对于大多数应用场景,推荐使用更高级的hdf5库而非直接使用hdf5-sys
  5. 需要系统安装HDF5库,可通过包管理器安装或从源码编译

性能建议

  1. 对于大型数据集,使用分块存储和压缩
  2. 批量写入数据比多次小量写入更高效
  3. 选择合适的数据类型减少存储空间和I/O时间
  4. 考虑使用集体操作(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(())
}

这个完整示例演示了:

  1. 创建HDF5文件并写入2D浮点数据集
  2. 读取HDF5文件并检查数据
  3. 添加和读取字符串属性
  4. 创建带压缩的分块数据集

每个函数都包含完整的错误处理和资源清理代码。

回到顶部