利用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://portal.hdfgroup.org/display/HDF5/Datatype+Basics
### 布局(Layout)
在HDF5檔案中, 布局(Layout)是指其儲存資料的方式,主要有以下三種:
* 連續佈局(Contiguous)
* 分塊佈局(Chunked)
* 緊湊佈局(Compact)
#### 連續佈局
連續佈局的實作上,是在儲存資料時不對資料進行分區,意即所有資料會被儲存在同一個分區之中。

#### 分塊佈局
分塊佈局在實作上是將資料分成一區一區的方式進行儲存,好處是可以有較好的延展性以及更好的可壓縮性。但壞處是在分塊過大或過小時會使得效能變得較差。


#### 緊湊佈局
緊湊佈局,指的是直接將資料寫在與資料標頭(Data header)相同的區域。好處是可以節省空間,但不適合用於儲存大量資料。通常用於儲存小資料集。

> 更多的描述以及細節,請參閱: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`