读取具有复合数据类型的 HDF5 数据集,其中包含多个具有可变数量的浮点数和整数的集合

Reading a HDF5 dataset with compound data type containing multiple sets with variable numbers of floats and ints

我正在尝试使用 C++ 中的 H5Cpp 库读取具有复合数据类型的 HDF5 数据集。 HDF5 文件是由模拟器创建的,所以不幸的是我无法控制结构。每个单元格包含多组可变长度浮点数和一组可变长度整数。 h5dump 显示的头部结构为:

DATASET "SET_NAME" {
         DATATYPE  H5T_COMPOUND {
            H5T_VLEN { H5T_IEEE_F64LE} "dProp1";
            H5T_VLEN { H5T_IEEE_F64LE} "dProp2";
            H5T_VLEN { H5T_STD_I32LE} "iProp1";
            H5T_VLEN { H5T_IEEE_F64LE} "dProp3";
         }
         DATASPACE  SIMPLE { ( 5, 122 ) / ( H5S_UNLIMITED, H5S_UNLIMITED ) }
}

我试图创建一个包含 VarLenTypeCompType,但是当 运行 抛出异常时,指出一个成员与另一个成员重叠(这就是我当任何地方给出的唯一大小是包含指针的结构的大小时会期望)。这是我到目前为止的摘录:

#include <H5Cpp.h>
typedef struct mytype_t {
    double* dprop1;
    double* dprop2;
    int* iprop1;
    double* dprop3;
} mytype_t;

H5::CompType ctype(sizeof(mytype_t));
auto double_type = H5::PredType::NATIVE_DOUBLE;
auto int_type = H5::PredType::NATIVE_INT;
auto vdouble_type = H5::VarLenType(&double_type);
auto vint_type = H5::VarLenType(&int_type);
ctype.insertMember("dProp1", HOFFSET(mytype_t, dprop1), vdouble_type);
ctype.insertMember("dProp2", HOFFSET(mytype_t, dprop2), vdouble_type);
ctype.insertMember("vProp1", HOFFSET(mytype_t, iprop1), vint_type);
ctype.insertMember("dProp3", HOFFSET(mytype_t, dprop3), vdouble_type);

std::vector<mytype_t> data_vector;
data_vector.resize(dims[0]*dims[1]);
dataset.read(data_vector.data(), ctype);

如何读取这样的数据集?

HDF5 文档和示例在这个问题上相当薄弱。

HDF5 要求用户保留可变长度数据的句柄(类型为 hvl_t),并在您将数组作为成员插入时引用该句柄到复合类型。

这引入了使这些句柄和您的向量、数组等与句柄保持同步的开销。也就是说,如果您保留动态数组,无论何时更改元素数量,都需要更新 "len" 字段,并且每当重新分配数组时,您都需要将 "p" 字段设置为新数组的第一个元素。

这也可与 std::vectors 一起使用,请记住您需要不断更新 plen 每当你 add/remove 元素进入向量时。 dguest 在 https://github.com/dguest/hdf5-ntuples/blob/master/include/h5container.hh 上有一段很好的代码可以做到这一点。

您的案例的解决方案(简化):

#include <H5Cpp.h>
typedef struct mytype_t {
    double* dProp;
    hvl_t dPropHandle;
    int* iProp;
    hvl_t iPropHandle;
} mytype_t;

H5::CompType ctype(sizeof(mytype_t));
auto double_type = H5::PredType::NATIVE_DOUBLE;
auto int_type = H5::PredType::NATIVE_INT;
auto vdouble_type = H5::VarLenType(&double_type);
auto vint_type = H5::VarLenType(&int_type);

// use handles instead of array pointers
ctype.insertMember("dProp", HOFFSET(mytype_t, dPropHandle), vdouble_type);
ctype.insertMember("iProp", HOFFSET(mytype_t, iPropHandle), vint_type);

std::vector<mytype_t> data_vector;
data_vector.resize(dims[0]*dims[1]);
dataset.read(data_vector.data(), ctype);

// update array pointers accordingly
for(mytype_t &m : data_vector)
{
    m.dProp = dPropHandle.p;
    m.iProp = iPropHandle.p;
}

使用向量:

#include <H5Cpp.h>
typedef struct mytype_t {
    std::vector<double> dProp;
    hvl_t dPropHandle;
    std::vector<int> iProp;
    hvl_t iPropHandle;
} mytype_t;

H5::CompType ctype(sizeof(mytype_t));
auto double_type = H5::PredType::NATIVE_DOUBLE;
auto int_type = H5::PredType::NATIVE_INT;
auto vdouble_type = H5::VarLenType(&double_type);
auto vint_type = H5::VarLenType(&int_type);

// use handles instead of array pointers
ctype.insertMember("dProp", HOFFSET(mytype_t, dPropHandle), vdouble_type);
ctype.insertMember("iProp", HOFFSET(mytype_t, iPropHandle), vint_type);

std::vector<mytype_t> data_vector;
data_vector.resize(dims[0]*dims[1]);
dataset.read(data_vector.data(), ctype);

// update vectors accordingly
for(mytype_t &m : data_vector)
{
    m.dProp.assign(static_cast<double*>(m.dPropHandle.p),
    static_cast<double*>(m.dPropHandle.p) + m.dPropHandle.len);

    m.iProp.assign(static_cast<int*>(m.iPropHandle.p),
    static_cast<int*>(m.iPropHandle.p) + m.iPropHandle.len);
}