4

A colleague of mine used labview to write an ASCII string as an attribute in an HDF5 file. I can see that the attribute exist, and read it, but I can't print it.

The attribute is, as shown in HDF Viewer:

Date = 2015\07\09

So "Date" is its name.

I'm trying to read the attribute with this code

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

Why is this happening? How can I get this string as a const char* or std::string?

I tried also using the type atype = H5Tcopy (H5T_C_S1);, and that didn't work too...

EDIT: Here I provide a full, self-contained program as it was requested:

#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;
}

Printed output of this program:

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...

Thanks.

The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189

3 Answers3

4

It turned out that H5Aread has to be called with a reference of the char pointer... so pointer of a pointer:

H5Aread(dateAttribHandler,atype,&date);

Keep in mind that one doesn't have to reserve memory for that. The library will reserve memory, and then you can free it with H5free_memory(date).

This worked fine.

EDIT:

I learned that this is the case only when the string to be read has variable length. If the string has a fixed length, then one has to manually reserve memory with size length+1 and even manually set the last char to null (to get a null-terminated string. There is a function in the hdf5 library that checks whether a string is fixed in length.

The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189
  • This is also true of writing using H5Awrite. The example [h5ex_t_vlstringatt.c](http://www.hdfgroup.org/ftp/HDF5/examples/examples-by-api/hdf5-examples/1_6/C/H5T/h5ex_t_vlstringatt.c) shows this, but it escaped me because of the "vector of strings" data they use. Also this is true of writing variable length strings to a dataset. – Liam Jun 15 '16 at 20:05
  • could you add the procedure for an if () on variable vs. fixed size, and tell us how to call the function to see if it's fixed or not? the docs take you down a long path. – markgalassi Jul 02 '22 at 20:13
1

I discovered that if you do not allocate date and pass the &date to H5Aread, then it works. (I use the C++ and python APIs, so I do not know the C api very well.) Specifically change:

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

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

And you should see 2015\07\09 printed.

You may want to consider using the C++ API. Using the C++ API, your example becomes:

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;
}
Phil
  • 5,822
  • 2
  • 31
  • 60
  • Thanks for the answer. I can't use the C++ API because I need to read an image in memory as shown in my example. Actually I found the solution by coincidence to replace `date` in H5Aread with `&date`... I don't understand why... it looks like a pointer of a pointer, but that works fine... – The Quantum Physicist Jul 10 '15 at 22:05
0

As a simpler alternative to existing APIs, your use-case could be solved as follows in C using HDFql:

// declare variable 'value'
char *value;

// register variable 'value' for subsequent use (by HDFql) 
hdfql_variable_register(&value);

// read 'Date' (from 'MagneticFields') and populate variable 'value' with it
hdfql_execute("SELECT FROM Example.hdf5 MagneticFields/Date INTO MEMORY 0");

// display value stored in variable 'value'
printf("Date=%s\n", value);

FYI, besides C, the code above can be used in C++, Python, Java, C#, Fortran or R with minimal changes.

SOG
  • 876
  • 6
  • 10