The dataspace is simple, i.e., it's an array which in this case
happens to have only one element. (This is NOT the same as a scalar attribute!)
Have a look at H5DreadTest2 in
for (int i = 0; i < rdata.Length; ++i)
{
int len = 0;
while (Marshal.ReadByte(rdata[i], len) != 0) { ++len; }
byte[] buffer = new byte[len];
Marshal.Copy(rdata[i], buffer, 0, buffer.Length);
string s = Encoding.UTF8.GetString(buffer);
Assert.IsTrue(s == ((string)m_utf8strings[i]));
Assert.IsTrue(H5.free_memory(rdata[i]) >= 0);
}
...
hnd.Free();
}
}
(The example is for datasets, but applies the same way for attributes.)
Your snippet is almost correct (i.e., allocate an IntPtr array of size 1).
The part that's wrong is that data needs no initialization or byte array allocation.
Reading a simple dataset of variable-length strings returns a pointer array.
(You must free the strings later or leak memory!)
G.
···
-----Original Message-----
From: Hdf-forum [mailto:hdf-forum-bounces@lists.hdfgroup.org] On Behalf Of Matt Wood
Sent: Thursday, March 30, 2017 10:24 AM
To: hdf-forum@lists.hdfgroup.org
Subject: Re: [Hdf-forum] Reading VLEN string attribute values.
Many thanks, that worked a treat. I have attached my working method below.
Matt
/// <summary>
/// Helper method that extracts a string array value from a variable length attribute.
/// See https://github.com/HDFGroup/HDF.PInvoke/blob/master/UnitTests/H5DTest/H5Dread.cs
/// </summary>
/// <param name="aid">The id of the attribute.</param>
/// <param name="tid">The type id of the attribute.</param>
/// <returns>An array of strings.</returns>
public string[] GetStrings(long aid, long tid)
{
long space = H5A.get_space(aid);
// TODO close space
long count = H5S.get_simple_extent_npoints(space);
// Create pointer [] to receive the addresses.
IntPtr[] rdata = new IntPtr[count];
// Create array for result.
string[] result = new string[count];
GCHandle hnd = GCHandle.Alloc(rdata, GCHandleType.Pinned);
// Call the function with address of pointer [].
H5A.read(aid, tid, hnd.AddrOfPinnedObject());
hnd.Free();
// For each pointer extract string.
for (int ii = 0; ii < rdata.Length; ++ii)
{
int len = 0;
// Find the end of the string (\0)
while (Marshal.ReadByte(rdata[ii], len) != 0) { ++len; }
byte[] buffer = new byte[len];
// Copy to buffer
Marshal.Copy(rdata[ii], buffer, 0, buffer.Length);
// Encode the string.
result[ii] = Encoding.UTF8.GetString(buffer);
}
return result;
}
Many thanks, that worked a treat. I have attached my working method below.
Matt
/// <summary>
/// Helper method that extracts a string array value from a variable length attribute.
/// See https://github.com/HDFGroup/HDF.PInvoke/blob/master/UnitTests/H5DTest/H5Dread.cs
/// </summary>
/// <param name="aid">The id of the attribute.</param>
/// <param name="tid">The type id of the attribute.</param>
/// <returns>An array of strings.</returns>
public string[] GetStrings(long aid, long tid)
{
long space = H5A.get_space(aid);
// TODO close space
long count = H5S.get_simple_extent_npoints(space);
// Create pointer [] to receive the addresses.
IntPtr[] rdata = new IntPtr[count];
// Create array for result.
string[] result = new string[count];
GCHandle hnd = GCHandle.Alloc(rdata, GCHandleType.Pinned);
// Call the function with address of pointer [].
H5A.read(aid, tid, hnd.AddrOfPinnedObject());
hnd.Free();
// For each pointer extract string.
for (int ii = 0; ii < rdata.Length; ++ii)
{
int len = 0;
// Find the end of the string (\0)
while (Marshal.ReadByte(rdata[ii], len) != 0) { ++len; }
byte[] buffer = new byte[len];
// Copy to buffer
Marshal.Copy(rdata[ii], buffer, 0, buffer.Length);
// Encode the string.
result[ii] = Encoding.UTF8.GetString(buffer);
}
return result;
}