C/C++ HDF5 读取字符串属性

Posted

技术标签:

【中文标题】C/C++ HDF5 读取字符串属性【英文标题】:C/C++ HDF5 Read string attribute 【发布时间】:2015-09-29 10:21:53 【问题描述】:

我的一位同事使用labview 将ASCII 字符串作为属性写入HDF5 文件。我可以看到该属性存在,并读取它,但我无法打印它。

属性为,如HDF Viewer所示:

日期 = 2015\07\09

所以“日期”就是它的名字。

我正在尝试使用此代码读取属性

hsize_t sz = H5Aget_storage_size(dateAttribHandler);
std::cout<<sz<<std::endl; //prints 16
hid_t atype = H5Aget_type(dateAttribHandler);
std::cout<<atype<<std::endl; //prints 50331867
std::cout<<H5Aread(dateAttribHandler,atype,(void*)date)<<std::endl; //prints 0
std::cout<<date<<std::endl; //prints messy characters!
//even with an std::string
std::string s(date);
std::cout<<s<<std::endl; //also prints a mess

为什么会这样?我怎样才能得到这个字符串作为const char*std::string

我也尝试使用 atype = H5Tcopy (H5T_C_S1); 类型,但也没有用...

编辑: 在这里,我根据要求提供了一个完整的、独立的程序:

#include <string>
#include <iostream>
#include <fstream>
#include <hdf5/serial/hdf5.h>
#include <hdf5/serial/hdf5_hl.h>

std::size_t GetFileSize(const std::string &filename)

    std::ifstream file(filename.c_str(), std::ios::binary | std::ios::ate);
    return file.tellg();


int ReadBinFileToString(const std::string &filename, std::string &data)

    std::fstream fileObject(filename.c_str(),std::ios::in | std::ios::binary);
    if(!fileObject.good())
    
        return 1;
    
    size_t filesize = GetFileSize(filename);
    data.resize(filesize);
    fileObject.read(&data.front(),filesize);
    fileObject.close();
    return 0;


int main(int argc, char *argv[])

    std::string filename("../Example.hdf5");
    std::string fileData;
    std::cout<<"Success read file into memory: "<<
               ReadBinFileToString(filename.c_str(),fileData)<<std::endl;

    hid_t handle;
    hid_t magFieldsDSHandle;
    hid_t dateAttribHandler;
    htri_t dateAtribExists;

    handle = H5LTopen_file_image((void*)fileData.c_str(),fileData.size(),H5LT_FILE_IMAGE_DONT_COPY | H5LT_FILE_IMAGE_DONT_RELEASE);
    magFieldsDSHandle = H5Dopen(handle,"MagneticFields",H5P_DEFAULT);
    dateAtribExists = H5Aexists(magFieldsDSHandle,"Date");
    if(dateAtribExists)
    
        dateAttribHandler = H5Aopen(magFieldsDSHandle,"Date",H5P_DEFAULT);
    


    std::cout<<"Reading file done."<<std::endl;
    std::cout<<"Open handler: "<<handle<<std::endl;
    std::cout<<"DS handler: "<<magFieldsDSHandle<<std::endl;
    std::cout<<"Attributes exists: "<<dateAtribExists<<std::endl;
    hsize_t sz = H5Aget_storage_size(dateAttribHandler);
    std::cout<<sz<<std::endl;
    char* date = new char[sz+1];
    std::cout<<"mem bef: "<<date<<std::endl;
    hid_t atype = H5Aget_type(dateAttribHandler);
    std::cout<<atype<<std::endl;
    std::cout<<H5Aread(dateAttribHandler,atype,(void*)date)<<std::endl;
    fprintf(stderr, "Attribute string read was '%s'\n", date);
    date[sz] = '\0';
    std::string s(date);
    std::cout<<"mem aft: "<<date<<std::endl;
    std::cout<<s<<std::endl;

    H5Dclose(magFieldsDSHandle);
    H5Fclose(handle);


    return 0;

这个程序的打印输出:

Success read file into memory: 0
Reading file done.
Open handler: 16777216
DS handler: 83886080
Attributes exists: 1
16
mem bef: 
50331867
0
Attribute string read was '�P7'
mem aft: �P7
�P7
Press <RETURN> to close this window...

谢谢。

【问题讨论】:

你如何定义“日期”?从 H5Aread 文档看来,日期需要是预先分配的内存缓冲区。喜欢这个 char date[1024] . @HughB 这是char* date = new char[sz] 。我试图使尺寸更大,但没有奏效。 您需要提供更完整的示例代码。什么是 C++ 日期变量?乱七八糟的字符是什么?第一个“2”是否正确读取? @phil 我在评论中提到了日期结构。接下来的字符是一些不可读的字符。这些信息是否足够?没有正确读取。 @Phil 所以这是你要求的完整程序,包括输出。 【参考方案1】:

原来H5Aread必须用char指针的引用来调用......所以指针的指针:

H5Aread(dateAttribHandler,atype,&date);

请记住,不必为此预留内存。该库将保留内存,然后您可以使用H5free_memory(date) 释放它。

这很好用。

编辑:

我了解到只有当要读取的字符串具有可变长度时才会出现这种情况。如果字符串具有固定长度,则必须手动保留大小为 length+1 的内存,甚至手动将最后一个字符设置为 null(以获取以 null 结尾的字符串。hdf5 库中有一个函数可以检查是否存在字符串的长度是固定的。

【讨论】:

使用 H5Awrite 编写也是如此。示例h5ex_t_vlstringatt.c 显示了这一点,但由于他们使用的“字符串向量”数据,它让我无法理解。将可变长度字符串写入数据集也是如此。【参考方案2】:

我发现如果你不分配日期并将 &date 传递给 H5Aread,那么它就可以工作。 (我用的是C++和python的API,所以对C api不是很了解。)具体改一下:

char* date = 0;
// std::cout<<"mem bef: "<<date<<std::endl;    

std::cout << H5Aread(dateAttribHandler, atype, &date) << std::endl;

您应该会看到 2015\07\09 打印出来。

您可能需要考虑使用 C++ API。使用 C++ API,您的示例变为:

std::string filename("c:/temp/Example.hdf5");
H5::H5File file(filename, H5F_ACC_RDONLY);
H5::DataSet ds_mag = file.openDataSet("MagneticFields");

if (ds_mag.attrExists("Date"))

    H5::Attribute attr_date = ds_mag.openAttribute("Date");
    H5::StrType stype = attr_date.getStrType();
    std::string date_str;
    attr_date.read(stype, date_str);
    std::cout << "date_str= <" << date_str << ">" << std::endl;

【讨论】:

感谢您的回答。我不能使用 C++ API,因为我需要读取内存中的图像,如我的示例所示。实际上我偶然发现了解决方案,将 H5Aread 中的 date 替换为 &amp;date... 我不明白为什么... 它看起来像一个指针的指针,但效果很好...

以上是关于C/C++ HDF5 读取字符串属性的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 h5py 从 HDF5 数据集中读取字符串

HDF5 简单读取数据集失败

如何从 HDF5 文件中读取属性?

将 hdf5 matlab 字符串加载到 Python 中

如何在 Matlab 中的 HDF5 属性字符串中放置换行符

使用 HDF5 保存要在 C++ 中读取的 MATLAB 结构