HDF5 파일 액세스 방법

HDF5에서 **루트 노드, 그룹, 데이터셋, 속성(Attributes)**는 모두 고유한 ID를 가지고 있습니다. 이 ID를 통해 객체를 접근할 수 있으며, 접근 방법은 두 가지입니다:

  1. 문자열 경로 (예: /Device1/temperature)
  2. 객체 ID (오픈 연산을 통해 얻은 정수형 ID)

이 두 가지 방법은 HDF5 작업의 핵심입니다. 아래에서 이 방법들의 원리와 사용법을 상세히 설명합니다.

1. HDF5 객체의 ID 본질

HDF5의 모든 객체는 오픈될 때 정수형 ID(부정수)를 받습니다. 이 ID는 현재 세션 중에서 객체를 참조하는 "핸들"이며, 이후의 작업(리드/ライト, 속성 추가, 닫기 등)에 사용됩니다.

  • 라이프사이클: ID는 객체가 "오픈될 때" 생성되고, "닫힐 때" 소멸됩니다(수동적으로 닫히지 않으면 메모리릭이 발생합니다).
  • 유일성: 같은 객체를 같은 세션에서 여러 번 오픈해도 다른 ID를 받지만, 같은 객체를 참조합니다. 다른 세션의 ID는 상호간섭 없습니다.

2. 경로와 ID를 통해 객체 접근 방법 비교

두 방법의 핵심 차이점은 "시작점"입니다. 두 방법 모두 최종적으로 같은 객체에 도달합니다:

접근 방법 원리 적합한 장면 예제 ( /Device1/temperature 데이터셋 접근)
문자열 경로 루트 노드나 특정 부모 객체에서 시작해 완전 경로로 직접 접근 객체의 완전 경로가 알려진 경우, 일회성 접근 H5D.open(fileId, "/Device1/temperature", H5P.DEFAULT)
객체 ID 부모 객체 ID를 먼저 획득한 후 상대 경로나 ID 체인을 통해 자식 객체에 접근 같은 객체를 여러 번 접근할 때(경로 해석을 피하기 위해) 1. 그룹 오픈: groupId = H5G.open(fileId, "/Device1")2. 그룹 ID를 통해 데이터셋 오픈: datasetId = H5D.open(groupId, "temperature")

3. 실전 예제: 두 가지 방법으로 객체 접근

아래 코드는 같은 데이터셋을 경로와 ID 체인 두 가지 방법으로 접근하고 데이터를 읽어오는 방법을 보여줍니다:

using System;
using HDF.PInvoke;

namespace HDF5PathVsIdDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            string hdf5Path = "path_vs_id_demo.h5";
            CreateTestFile(hdf5Path); // 테스트 파일 생성

            // 방법 1: 완전 경로를 통해 데이터셋 접근
            Console.WriteLine("=== 방법 1: 완전 경로를 통해 접근 ===");
            using (var file = new HDF5File(hdf5Path, H5F.ACC_RDONLY))
            {
                string datasetPath = "/Device1/temperature";
                int datasetId = H5D_Open(file.ID, datasetPath, H5P.DEFAULT);
                ReadDatasetData(datasetId);
                H5D_Close(datasetId);
            }

            // 방법 2: ID 체인(부모 ID + 상대 경로)을 통해 데이터셋 접근
            Console.WriteLine("\n=== 방법 2: ID 체인을 통해 접근 ===");
            using (var file = new HDF5File(hdf5Path, H5F.ACC_RDONLY))
            {
                // 1. 먼저 그룹 오픈(그룹 ID 획득)
                string groupPath = "/Device1";
                int groupID = H5G_Open(file.ID, groupPath, H5P.DEFAULT);

                // 2. 그룹 ID를 통해 상대 경로로 데이터셋 오픈
                string datasetName = "temperature"; // 상대 경로(그룹 내에서 절대 경로)
                int datasetId = H5D_Open(groupID, datasetName, H5P.DEFAULT);

                ReadDatasetData(datasetId);

                // 닫기 순서: 자식부터 부모 순으로
                H5D_Close(datasetId);
                H5G_Close(groupID);
            }
        }

        // 테스트 파일 생성: /Device1/temperature에 温度 데이터 저장
        static void CreateTestFile(string path)
        {
            int fileID = H5F_Create(path, H5F.ACC_TRUNC, H5P.DEFAULT, H5P.DEFAULT);
            int groupID = H5G_Create(fileID, "/Device1", H5P.DEFAULT, H5P.DEFAULT, H5P.DEFAULT);

            float[] data = { 23.5f, 24.1f, 23.9f };
            long[] dims = { data.Length };
            int spaceID = H5S_CreateSimple(1, dims, null);
            int datasetID = H5D_Create(groupID, "temperature", H5T.IEEE_F32LE, spaceID, H5P.DEFAULT, H5P.DEFAULT, H5P.DEFAULT);
            H5D_Write(datasetID, H5T.IEEE_F32LE, H5S.ALL, H5S.ALL, H5P.DEFAULT, data);

            // 리소스 닫기
            H5D_Close(datasetID);
            H5S_Close(spaceID);
            H5G_Close(groupID);
            H5F_Close(fileID);
        }

        // 데이터셋 데이터 읽기 및 출력
        static void ReadDatasetData(int datasetID)
        {
            int spaceID = H5D_GetSpace(datasetID);
            long[] dims = new long[1];
            H5S_GetSimpleExtentDims(spaceID, dims, null);
            float[] data = new float[dims[0]];
            H5D_Read(datasetID, H5T.IEEE_F32LE, H5S.ALL, H5S.ALL, H5P.DEFAULT, data);

            Console.Write("데이터: ");
            foreach (var value in data)
            {
                Console.Write($"{value} ");
            }
            Console.WriteLine();

            H5S_Close(spaceID);
        }
    }

    // 간단히 HDF5 파일 작업을 封裝한 클래스, 자동으로 닫기 수행
    class HDF5File : IDisposable
    {
        public int ID { get; private set; }
        public HDF5File(string path, H5F.accesstype access)
        {
            ID = H5F_Open(path, access, H5P.DEFAULT);
        }
        public void Dispose()
        {
            if (ID >= 0)
                H5F_Close(ID);
        }
    }
}

4. 실행 결과

=== 방법 1: 완전 경로를 통해 접근 ===
데이터: 23.5 24.1 23.9 

=== 방법 2: ID 체인을 통해 접근 ===
데이터: 23.5 24.1 23.9 

두 방법 모두 같은 데이터를 읽어내며, 경로와 ID가 객체 접근에 동등하게 유효하다는 것을 입증합니다.

5. 중요 세부사항 및 최선의实践

  1. 루트 노드의 ID는 파일 ID와 같음 루트 노드(/)는 별도의 오픈 메서드가 없으며, ID는 파일 ID와 같습니다. 예를 들어 루트 노드 ID를 통해 그룹을 오픈할 수 있습니다:
// 파일 ID(루트 노드 ID)를 통해 그룹 오픈
int groupID = H5G_Open(fileID, "/Device1", H5P.DEFAULT); 
  1. 속성(Attributes)의 접근도 두 가지 방법 지원 속성은 반드시某个 객체(루트/그룹/데이터셋)에 기반해야 하며, 접근 시 해당 객체의 ID를 먼저 획득해야 합니다:
// 데이터셋 ID를 통해 속성 오픈
int attrID = H5A_Open(datasetID, "Unit"); 
  1. ID의 라이프사이클 관리가 핵심
  • 모든 ID는 수동적으로 닫아야 하며, "자식 객체부터 부모 객체 순으로" 닫는 것이 중요합니다(예: 데이터셋 → 그룹 → 파일).
  • try-finallyIDisposable을 활용하여 닫기 보장(예제 중의 HDF5File 클래스 참조).
  1. 접근 방법의 선택
  • 단일 접근: 경로 사용이 더 간편합니다(한 줄의 코드로 바로的目的에 도달).
  • 같은 객체나 그 자식 객체를 여러 번 접근할 때: ID 체인 사용이 효율적입니다(경로 문자열을 반복적으로 해석하는 부담을 없애기 위해).

6. 요약

  • ID는 HDF5 객체의 "작업 핸들": 루트 노드, 그룹, 데이터셋, 속성 모두 ID를 가지고 있습니다. 이 ID를 통해 모든 작업을 수행할 수 있습니다.
  • 경로는 객체의 "위치 주소": 경로를 통해 ID를 획득한 후 작업을 수행할 수 있습니다.
  • 두 방법은 동등한 접근 방법: 어떤 방법을 선택하느냐는 사용 상황에 따라 달라집니다(단일 접근은 경로, 반복적 접근은 ID 체인이 유리).

HDF5 작업을 이해하려면 경로와 ID의 관계를 명확히 이해하는 것이 핵심입니다. 앞으로 더 복잡한 구조를 다룰 때에도 이 두 가지 방법은 기본이 됩니다.

태그: HDF5 데이터셋 속성 객체ID 경로

7월 15일 23:55에 게시됨