利用C語言對HDF5資料進行操作 === $\qquad$在Python風行的時代,利用[h5py](https://docs.h5py.org/en/stable/)對HDF5類型的檔案進行操作非常的容易。然而在主要程式不是利用Python進行操作的場景,直接利用該語言進行操作可以省去多一個轉換的步驟。這是一份紀錄如何利用C語言對HDF5檔案進行建立、寫入以及增補資料集的筆記。 ## 建立資料集 利用C語言建立HDF5檔案最基本的幾個要素有: * 引用`hdf5.h`標頭檔用以取得相關設定及函數。 * 宣告`hid_t`類變數用以處理資料相關變數。 * 宣告`herr_t`類變數用以處理各種回傳值。 * 宣告`hsize_t`類變數用以定義資料集的維度等資訊。 在HDF5的操作上,主要有幾種函數接口(API)類別: * `H5`: 最基本的函數都被放置在本類別之下。 * `H5A`:關於註解以及屬性相關的函數定義在此類別中。 * `H5D`:對資料集的相關操作所需要的函數。 * `H5F`:對於檔案的操作所需要的函數可以在這裡找到。 * `H5G`:對於資料集的群組操作的函數被包含在此類別之下。 * `H5E`:對於異常狀況的處理函數被歸類在此類別之下。 除了以上類別,還有其他的類別存在。 > 所有的API類別可以在[官方資料](https://portal.hdfgroup.org/display/HDF5/The+HDF5+API)找到。 以下是一個簡單的範例,在這範例中建立了一個最簡單的HDF5檔案。 ```c= /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright by The HDF Group. * * Copyright by the Board of Trustees of the University of Illinois. * * All rights reserved. * * * * This file is part of HDF5. The full HDF5 copyright notice, including * * terms governing use, modification, and redistribution, is contained in * * the COPYING file, which can be found at the root of the source code * * distribution tree, or in https://www.hdfgroup.org/licenses. * * If you do not have access to either file, you may request a copy from * * help@hdfgroup.org. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "hdf5.h" #define FILE "dset.h5" int main() { hid_t file_id, dataset_id; /* identifiers */ herr_t status; int i, j, dset_data[4][6]; /* Initialize the dataset. */ for (i = 0; i < 4; i++) for (j = 0; j < 6; j++) dset_data[i][j] = i * 6 + j + 1; /* Open an existing file. */ file_id = H5Fopen(FILE, H5F_ACC_RDWR, H5P_DEFAULT); /* Open an existing dataset. */ dataset_id = H5Dopen2(file_id, "/dset", H5P_DEFAULT); /* Write the dataset. */ status = H5Dwrite(dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset_data); status = H5Dread(dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset_data); /* Close the dataset. */ status = H5Dclose(dataset_id); /* Close the file. */ status = H5Fclose(file_id); } ``` $\qquad$在上方的範例中,我們建立了`file_id`以及`dataset_id`用以操作資料集以及檔案,併利用一個`herr_t`類別的函數`status`來接收函數執行的狀況(異常或是正常運行都利用這個變數進行檢查)。在第18行及第21行,我們建立了輸出HDF5檔案以及資料集的對應物件,這個物件包含了資料對應的記憶體位置,用來告知程式要將資料寫入記憶體的哪一區段。其中,在第21行中,`"/dest"`的用途是宣告資料將被放在`"/dest"`的路徑之下。在第24行中,我們將事先初始化過的二維陣列寫入先前位於`dataset_id`的資料物件中,併利用`status`變數來確認此操作是否成功。在完成寫入資料候,在第29行及第32行,我們將先前宣告的檔案以及資料夾物件進行關閉。這樣我們就成功將一個維度`4x6`的陣列寫入了`dset.h5`檔案中。 $\qquad$眼尖的人或許有發現我們並未在此範例中宣告`hsize_t`類別的變數。這是因為我們並未在此範例中對`Dataspace`進行操作以及宣告。這是因為對HDF5檔案而言,`Dataspace`並不是一個必須要宣告的部份。相關描述可以在[相關文件](https://support.hdfgroup.org/HDF5/doc/H5.intro.html#:~:text=Dataspace.%20A%20dataset%20dataspace%20describes%20the%20dimensionality%20of%20the%20dataset.%20The%20dimensions%20of%20a%20dataset%20can%20be%20fixed%20(unchanging)%2C%20or%20they%20may%20be%20unlimited%2C%20which%20means%20that%20they%20are%20extendible%20(i.e.%20they%20can%20grow%20larger).)中找到。 ## 資料型別以及佈局 ### 資料型別 在HDF5檔案中,有兩大種類的型別可以被用以儲存資料: * 標準型別(Standard) * 原子(Atomic)型別 * 複合(Composite)型別 * 原生型別(Native) 其中,**標準型別**指的是程式語言所提供的標準資料型別,例如:**整數**(int)、**浮點數**(float)等。其中複合型別指的是**陣列**等複合結構物件。而**原生型別**是指由HDF5函式庫所預先定義的資料型別。在宣告上,區分上述兩大類型別的方式為:`H5T_<Category>_<Data_type>`。下方表格是在C語言中可以使用的資料型別。 $\qquad\quad$![表一:資料型別](https://i.imgur.com/1e5GJ2F.png) > 更多的描述以及細節,請參閱:https://portal.hdfgroup.org/display/HDF5/Datatype+Basics ### 布局(Layout) 在HDF5檔案中, 布局(Layout)是指其儲存資料的方式,主要有以下三種: * 連續佈局(Contiguous) * 分塊佈局(Chunked) * 緊湊佈局(Compact) #### 連續佈局 連續佈局的實作上,是在儲存資料時不對資料進行分區,意即所有資料會被儲存在同一個分區之中。 ![](https://i.imgur.com/JrR1ZTr.png) #### 分塊佈局 分塊佈局在實作上是將資料分成一區一區的方式進行儲存,好處是可以有較好的延展性以及更好的可壓縮性。但壞處是在分塊過大或過小時會使得效能變得較差。 ![](https://i.imgur.com/6voPYJD.png) ![](https://i.imgur.com/y4iVLkT.png) #### 緊湊佈局 緊湊佈局,指的是直接將資料寫在與資料標頭(Data header)相同的區域。好處是可以節省空間,但不適合用於儲存大量資料。通常用於儲存小資料集。 ![](https://i.imgur.com/O8DxRWl.png) > 更多的描述以及細節,請參閱:https://portal.hdfgroup.org/display/HDF5/Dataset+Storage+Layout ## 範例 ### 建立緊湊資料集 ```c= /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright by The HDF Group. * * Copyright by the Board of Trustees of the University of Illinois. * * All rights reserved. * * * * This file is part of HDF5. The full HDF5 copyright notice, including * * terms governing use, modification, and redistribution, is contained in * * the COPYING file, which can be found at the root of the source code * * distribution tree, or in https://www.hdfgroup.org/licenses. * * If you do not have access to either file, you may request a copy from * * help@hdfgroup.org. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "hdf5.h" #include <stdio.h> #include <stdlib.h> #define FILE "h5ex_d_compact.h5" #define DATASET "DS1" #define DIM0 4 #define DIM1 7 int main (void) { hid_t file, space, dset, dcpl; /* Handles */ herr_t status; H5D_layout_t layout; hsize_t dims[2] = {DIM0, DIM1}; int wdata[DIM0][DIM1], /* Write buffer */ rdata[DIM0][DIM1], /* Read buffer */ i, j; /* * Initialize data. */ for (i=0; i<DIM0; i++) for (j=0; j<DIM1; j++) wdata[i][j] = i * j - j; /* * Create a new file using the default properties. */ file = H5Fcreate (FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); /* * Create dataspace. Setting maximum size to NULL sets the maximum * size to be the current size. */ space = H5Screate_simple (2, dims, NULL); /* * Create the dataset creation property list, set the layout to * compact. */ dcpl = H5Pcreate (H5P_DATASET_CREATE); status = H5Pset_layout (dcpl, H5D_COMPACT); /* * Create the dataset. We will use all default properties for this * example. */ dset = H5Dcreate (file, DATASET, H5T_STD_I32LE, space, H5P_DEFAULT, dcpl, H5P_DEFAULT); /* * Write the data to the dataset. */ status = H5Dwrite (dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wdata[0]); /* * Close and release resources. */ status = H5Pclose (dcpl); status = H5Dclose (dset); status = H5Sclose (space); status = H5Fclose (file); /* * Now we begin the read section of this example. */ /* * Open file and dataset using the default properties. */ file = H5Fopen (FILE, H5F_ACC_RDONLY, H5P_DEFAULT); dset = H5Dopen (file, DATASET, H5P_DEFAULT); /* * Retrieve the dataset creation property list, and print the * storage layout. */ dcpl = H5Dget_create_plist (dset); layout = H5Pget_layout (dcpl); printf ("Storage layout for %s is: ", DATASET); switch (layout) { case H5D_COMPACT: printf ("H5D_COMPACT\n"); break; case H5D_CONTIGUOUS: printf ("H5D_CONTIGUOUS\n"); break; case H5D_CHUNKED: printf ("H5D_CHUNKED\n"); break; case H5D_VIRTUAL: printf ("H5D_VIRTUAL\n"); break; case H5D_LAYOUT_ERROR: case H5D_NLAYOUTS: printf ("H5D_LAYOUT_ERROR\n"); } /* * Read the data using the default properties. */ status = H5Dread (dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rdata[0]); /* * Output the data to the screen. */ printf ("%s:\n", DATASET); for (i=0; i<DIM0; i++) { printf (" ["); for (j=0; j<DIM1; j++) printf (" %3d", rdata[i][j]); printf ("]\n"); } /* * Close and release resources. */ status = H5Pclose (dcpl); status = H5Dclose (dset); status = H5Fclose (file); return 0; } ``` ### 建立資料群組 ```c= /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright by The HDF Group. * * Copyright by the Board of Trustees of the University of Illinois. * * All rights reserved. * * * * This file is part of HDF5. The full HDF5 copyright notice, including * * terms governing use, modification, and redistribution, is contained in * * the COPYING file, which can be found at the root of the source code * * distribution tree, or in https://www.hdfgroup.org/licenses. * * If you do not have access to either file, you may request a copy from * * help@hdfgroup.org. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * This example illustrates how to create and close a group. * It is used in the HDF5 Tutorial. */ #include "hdf5.h" #define FILE "group.h5" int main() { hid_t file_id, group_id; /* identifiers */ herr_t status; /* Create a new file using default properties. */ file_id = H5Fcreate(FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); /* Create a group named "/MyGroup" in the file. */ group_id = H5Gcreate2(file_id, "/MyGroup", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); /* Close the group. */ status = H5Gclose(group_id); /* Terminate access to the file. */ status = H5Fclose(file_id); } ``` ## Reference 1. [HDF5 官方文件](https://portal.hdfgroup.org/display/support) 2. [HDF5 Workshop Slide](https://www.slideserve.com/tulia/hdf5-datasets-and-i-o) 3. [HDF5 官方範例](https://portal.hdfgroup.org/display/HDF5/Examples+by+API) ###### tags: `C` `HDF5`