如何使用HDF5DOTNET制作复合数据类型?

时间:2019-11-29 23:47:45

标签: c# hdf5 hdf5dotnet

当我将包含数组的结构写入HDF5数据集时遇到问题。首先,窗口表单不是以以下行开头:

H5T.insert(typeStruct, "string", 0, H5T.create_array(new H5DataTypeId(H5T.H5Type.C_S1), dims2));

窗口窗体至少从没有行开始,所以我认为定义复合数据类型有问题。我研究了手册和许多示例,但是仍然无法解决问题。我可以举一个使用复合数据类型在C#中编写具有多个数组的结构的示例吗?

using HDF5DotNet;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;


namespace WindowsFormsApplication1
{
    public unsafe partial class Form1 : Form
    {

        public unsafe struct struct_TR
        {
            public string[] arr_currentLong;

            public struct_TR(byte size_currentTime)
            {
                arr_currentLong = new string[size_currentTime];
            }
        }


        public Form1()
        {
            InitializeComponent();

            long ARRAY_SIZE = 255;
            struct_TR structMade = new struct_TR(255);

            for (int i = 0; i < 255; i++)
            {
                structMade.arr_currentLong[i] = i.ToString();
            }

            string currentPath = Path.GetDirectoryName(Application.ExecutablePath);     
            Directory.SetCurrentDirectory(currentPath);                                 


            H5FileId fileId = H5F.create(@"weights.h5", H5F.CreateMode.ACC_TRUNC);

            long[] dims1 = { 1 };
            long[] dims2 = { 1, ARRAY_SIZE };

            H5DataSpaceId myDataSpace = H5S.create_simple(1, dims1);

            H5DataTypeId string_type = H5T.copy(H5T.H5Type.C_S1);
            H5DataTypeId array_tid1 = H5T.create_array(string_type, dims2);

            H5DataTypeId typeStruct = H5T.create(H5T.CreateClass.COMPOUND, Marshal.SizeOf(typeof(struct_TR)));
            H5T.insert(typeStruct, "string", 0, H5T.create_array(new H5DataTypeId(H5T.H5Type.C_S1), dims2));

            H5DataSetId myDataSet = H5D.create(fileId, "/dset", typeStruct, myDataSpace);

            H5D.writeScalar<struct_TR>(myDataSet, typeStruct, ref structMade);

        }
    }
}

1 个答案:

答案 0 :(得分:2)

我知道如何用数组保存结构的唯一方法是创建一个常量数组 例如,这是一个长度为4的结构。

    [StructLayout(LayoutKind.Sequential)]
    public struct Responses
    {
        public Int64 MCID;
        public int PanelIdx;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public short[] ResponseValues;
    }

在这里创建了一个包含数组的4个结构的数组:

       responseList = new Responses[4] {
            new Responses() { MCID=1,PanelIdx=5,ResponseValues=new short[4]{ 1,2,3,4} },
            new Responses() { MCID=2,PanelIdx=6,ResponseValues=new short[4]{ 5,6,7,8}},
            new Responses() { MCID=3,PanelIdx=7,ResponseValues=new short[4]{ 1,2,3,4}},
            new Responses() { MCID=4,PanelIdx=8,ResponseValues=new short[4]{ 5,6,7,8}}
        };

以下代码行将结构数组写入HDF5文件:

        string filename = "testArrayCompounds.H5";
        var fileId =H5F.create(filename, H5F.ACC_TRUNC);
        var status = WriteCompounds(fileId, "/test", responseList);
        H5F.close(fileId);

WriteCompounds方法如下:

    public static int WriteCompounds<T>(hid_t groupId, string name, IEnumerable<T> list) //where T : struct
    {
        Type type = typeof(T);
        var size = Marshal.SizeOf(type);
        var cnt = list.Count();

        var typeId = CreateType(type);

        var log10 = (int)Math.Log10(cnt);
        ulong pow = (ulong)Math.Pow(10, log10);
        ulong c_s = Math.Min(1000, pow);
        ulong[] chunk_size = new ulong[] { c_s };

        ulong[] dims = new ulong[] { (ulong)cnt };

        long dcpl = 0;
        if (!list.Any() || log10 == 0) { }
        else
        {
            dcpl = CreateProperty(chunk_size);
        }

        // Create dataspace.  Setting maximum size to NULL sets the maximum
        // size to be the current size.
        var spaceId = H5S.create_simple(dims.Length, dims, null);

        // Create the dataset and write the compound data to it.
        var datasetId = H5D.create(groupId, name, typeId, spaceId, H5P.DEFAULT, dcpl);

        IntPtr p = Marshal.AllocHGlobal(size * (int)dims[0]);

        var ms = new MemoryStream();
        BinaryWriter writer = new BinaryWriter(ms);
        foreach (var strct in list)
            writer.Write(getBytes(strct));
        var bytes = ms.ToArray();

        GCHandle hnd = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        var statusId = H5D.write(datasetId, typeId, spaceId, H5S.ALL,
            H5P.DEFAULT, hnd.AddrOfPinnedObject());

        hnd.Free();
        /*
         * Close and release resources.
         */
        H5D.close(datasetId);
        H5S.close(spaceId);
        H5T.close(typeId);
        H5P.close(dcpl);
        Marshal.FreeHGlobal(p);
        return statusId;
    }

需要三个附加的帮助功能,此处显示两个:

    private static long CreateType(Type t)
    {
        var size = Marshal.SizeOf(t);
        var float_size = Marshal.SizeOf(typeof(float));
        var int_size = Marshal.SizeOf(typeof(int));
        var typeId = H5T.create(H5T.class_t.COMPOUND, new IntPtr(size));

        var compoundInfo = Hdf5.GetCompoundInfo(t);
        foreach (var cmp in compoundInfo)
        {
            H5T.insert(typeId, cmp.name, Marshal.OffsetOf(t, cmp.name), cmp.datatype);
        }
        return typeId;
    }

    private static long CreateProperty(ulong[] chunk_size)
    {
        var dcpl = H5P.create(H5P.DATASET_CREATE);
        H5P.set_layout(dcpl, H5D.layout_t.CHUNKED);
        H5P.set_chunk(dcpl, chunk_size.Length, chunk_size);
        H5P.set_deflate(dcpl, 6);
        return dcpl;
    }

我还有一个ReadCompounds来读取hdf5文件。 CreateType方法中使用的Hdf5.GetCompoundInfo方法也很长。所以在这里我不会显示这些方法。

因此,用于编写某些结构的代码很多。 我制作了一个名为HDF5DotnetTools的库,该库使您可以轻松地读写类和结构。在这里您还可以找到ReadCompounds和GetCompoundInfo方法。

HDF5DotnetTools的单元测试中,您还可以找到有关如何使用数组编写类的示例