기술에 대한 문의사항은 아래 메일로 연락주시기 바랍니다.


sales@selab.co.kr


아래 사용자 커뮤니티를 통해서도 기술 및 사용법 관련 정보를 얻을 수 있습니다.





사용자 커뮤니티

*배너를 클릭하시면 링크로 이동합니다.


ENVI 튜토리얼

(Youtube 에스이랩 인공지능연구실)

이상우의 IDL 블로그

IDL/ENVI/SARscape 사용자 포럼






사용자 커뮤니티 소식

*커뮤니티에 게시된 새 글 미리보기입니다.

링크를 클릭하시면 본 게시물로 이동합니다.


ASSOC 함수를 이용한 바이너리 파일의 Random Access

2025-04-17
조회수 84

이상우의 IDL 블로그에, Yale Bright Star Catalog를 읽는 방법에 대해 소개가 되어 있습니다. 

원문 링크 : https://swrush.tistory.com/788 (Yale Bright Star Catalog 데이터 읽기 - 개선된 방법)

Yale Bright Star Catalog는 천문학자들이 사용하는 별 목록이지만, 이 글에서는 천문학을 설명하려는 것이 아니고, 흔히 Binary 파일이라고 언급하는 파일을 읽는 방법에 대해 설명합니다. 블로그의 글에서 (1)과 (2)는 Binary 파일의 구조를 설명하기 위해 일부러 비효율적인 방법을 쓰고 있습니다. 몇 byte를 건너 뛰면 우리가 읽고자 하는 데이터가 거기에 있고, 여기에서 몇 byte를 읽어내면 우리가 원하는 데이터를 뽑아낼 수 있다는 의미의 설명입니다. 그리고 (개선된 방법)이라고 추가한 글에서 일반적인 Binary 파일 읽기의 방법을 설명하고 있습니다. 속도 차이가 많이 납니다.  

ASCII 파일이 메모장으로 열면 읽을 수 있는 "글자들'로 이루어진 데에 반해, Binary 파일은 데이터 type 대로 파일을 써 놓은 것입니다. 

  • ASCII 파일 : Formatted 라고도 합니다.  IDL에서 readF 프로시저로 읽는데 이 때 F가 Formatted를 의미합니다. 일반적으로 처음부터 순차적으로 읽어내는 것을 목적으로 합니다.  Binary 파일에 비해 같은 내용이라도 파일 크기가 커집니다. 그렇지만 사람이 메모장 등으로 열어 보기 편합니다.  사람이 읽기 편하다는 장점은 무시할 수 없는 장점입니다.
  • Binary 파일 : Unformatted 라고도 합니다. IDL에서 readU 프로시저로 읽는데 이 때 U가 Unformatted를 의미합니다. 처음부터 순차적으로 파일을 쓰긴 하지만, 읽을 때는 앞 뒤를 자유롭게 오고갈 수 있는 것이 큰 장점입니다. 그래서 Random Access File이라고도 합니다. 일반적으로 ASCII 파일에 비해 같은 내용이라도 파일 크기가 작습니다. 그렇지만 컴퓨터 친화적이어서, 사람이 메모장으로 열어 보면 그 내용을 알아보기 어렵습니다.  파일 크기도 작지만, 컴퓨터 친화적인 파일이라 읽고 쓰기도 ASCII에 비해 훨씬 빠릅니다. 

Binary 파일을 읽기 위한 설명은 Yale Bright Star Catalog 데이터 예시도 있지만, 같은 블로그에 다른 예제들도 있습니다. 

원문 링크 : https://swrush.tistory.com/441 (IDL에서 Binary 파일 읽기 [1] [2] [3])

메모장으로 열어서 눈에 들어오지 않으니, 처음에는 어려운 느낌이 들지만, 위 블로그의 내용을 읽어 보면, 규칙만 지키면 어려울 것이 전혀 없습니다. 예를 들어,  16bit Integer는 IDL의 Integer로 읽는다, 32bit 실수는 IDL의 Float로 읽는다와 같은 규칙 뿐입니다. 이것은 IDL만 위한 파일 형식도 아니고, 세상 모든 프로그래밍 언어들이 그렇게 읽고 쓰는 것입니다. 그래서 다른 프로그래밍 언어에서도 읽고 쓰는 방식이 비슷합니다. 

기본적인 부분은 위 링크의 블로그에 친절히 잘 설명이 되어 있으므로, 이 글에서는 Binary 파일의 Random Access 특성에 대해 말씀드리고자 합니다. 

ASCII 파일을 먼저 잠깐 얘기하자면, 이 파일의 읽기는 원리적으로 한 줄씩 읽어 내려가는 것입니다. 파일 속에 줄바꿈 기호가 들어 있거든요. 그런데 한 줄의 길이는 정해져 있지 않습니다. 긴 줄도 있고 짧은 줄도 있습니다. 그러다 보니 빠른 속도로 파일의 앞뒤를 왔다갔다 할 수 없습니다. 한 줄의 단위를 정하려면 파일의 모든 바이트 속에서 줄바꿈 기호를 찾아야 하니까요. 기술적으로 거꾸로 몇 줄을 돌이켜 올라가는 것이 안될 것은 없지만, 그러려면 파일의 모든 바이트를 뒤져서 줄바꿈 기호를 찾아야 합니다. 되긴 되겠지만 비효율적이고, 그래서 이렇게 안합니다.   

Binary 파일은 이미 약속이 정해져 있습니다. 몇 바이트로 이루어진 어떤 데이터 타입의 데이터가 얼마만큼 기록되어 있다는 것을 알려 줘야 파일을 읽을 수 있습니다. 이 내용이 없으면 파일을 읽기 위해서는 암호 해독에 가까운 작업을 해야 합니다. 그러니 바이너리 파일은, 쓸 때(Writing 할 때) 정했던 규칙은 공유 되어야 읽을 수 있습니다. 이 규칙을 준수해서 쓴 파일이기 때문에, 몇 번째 바이트로 가면 어떤 내용이 나올지 예상이 되는 포맷이 됩니다. 100줄 째 내용을 읽다가 다시 앞으로 와서 30번째 줄을 읽고, 앞 뒤로 왔다갔다 하는 것이 자유로운 포맷이 되는 장점을 가집니다. (Random Access File).

File 안에서 byte 단위로 포인터 위치를 이동시키는 IDL 명령은 Point_LUN 입니다. 이 명령어를 이용해서 Low Level에서의 File의 Random Access를 할 수 있습니다.

파일이, Yale Bright Star Catalog와 같이 정형화된 레코드(한 줄) 단위로 기록된 것이라면, 이 레코드 단위로 파일을 Random Access 할 수 있는 방법이 있는데, ASSOC 함수를 사용하는 것입니다. 

IDL ASSOC : https://www.nv5geospatialsoftware.com/docs/ASSOC.html

ASSOC 은 Associate를 줄인 이름이고, 이 단어에 연관짓다, 연동시키다의 뜻으로 이해할 수 있습니다. 

이 함수는, 한 줄(record 단위)의 형식을 정의해 주고나서, 파일 전체를 이러한 record 들의 배열(array)로 인식하게 연동시킵니다. 

Yale Bright Star Catalog의 데이터 포맷에서 한 줄은 아래와 같이 정의할 수 있습니다. 

one_record = {cnum: 0.0, ra_rad: 0.0d, dec_rad: 0.0d, type: BYTARR(2), vmag:0, pm_ra:0.0, pm_dec:0.0}

이제 파일을 이 one_record 단위로 연동하는 방법은 다음과 같습니다. 

file='bsc5'

one_record = {cnum: 0.0, ra_rad: 0.0d, dec_rad: 0.0d, type: BYTARR(2), vmag:0, pm_ra:0.0, pm_dec:0.0}

openr, 1, file

data = assoc(1, one_record, 28, /PACKED)

파일 번호 1번을 one_record 변수에 있는 구조체의 단위로 access 할 것이고, 앞의 28 바이트는 (header part 이므로), 이 데이터 구조에 사용하지 않는다는 설정입니다. 

사실은 파일이지만, 이제 IDL 에서 이 파일은 data 변수를 배열처럼 사용해서 접근할 수 있습니다. 

; Accessing 1st record.

this_record = data[0] 

print, FIX(this_record.cnum), this_record.ra_rad*!radeg, this_record.dec_rad*!radeg, ' ',$

 string(this_record.type), this_record.vmag/100., this_record.pm_ra*!radeg, this_record.pm_dec*!radeg


; Accessing 5th record.

this_record = data[4]

print, FIX(this_record.cnum), this_record.ra_rad*!radeg, this_record.dec_rad*!radeg, ' ',$

  string(this_record.type), this_record.vmag/100., this_record.pm_ra*!radeg, this_record.pm_dec*!radeg


; Accessing 9000th record

this_record = data[8999]

print, FIX(this_record.cnum), this_record.ra_rad*!radeg, this_record.dec_rad*!radeg, ' ', $

  string(this_record.type), this_record.vmag/100., this_record.pm_ra*!radeg, this_record.pm_dec*!radeg


; Accessing 3rd record.

this_record = data[2]

print, FIX(this_record.cnum), this_record.ra_rad*!radeg, this_record.dec_rad*!radeg, ' ',$

  string(this_record.type), this_record.vmag/100., this_record.pm_ra*!radeg, this_record.pm_dec*!radeg


실행 결과는 다음과 같습니다.

       1       1.2912499       45.229164 A1      6.70000 -3.33333e-06 -5.00000e-06

       5       1.5666666       58.436663 G5      5.96000  7.30555e-05  8.33333e-06

    9000       356.20123       55.799719 G4      6.51000  5.55556e-06 -1.38889e-06

       3       1.3337499      -5.7074997 K0      4.61000 -2.50000e-06  2.47222e-05


처음에 구조체를 한번 설정하는 것이 귀찮아 보일 수 있으나(사실 그렇지도 않은 것이 결국 Binary 파일을 읽으려면 이 정도 설정은 꼭 필요합니다), 이 방식이 논리적으로 친근하고, Random Access를 위한 어떤 계산 과정이 필요 없다는 점에서 사용이 쉬운 면도 있습니다.

예제의 Yale Bright Star Catalog 같은 경우  크기가 300KB도 안되는 작은 데이터여서, 한번에 전체를 다 읽어서 메모리에 배열로 올려 놓는 것이 가장 효율적일 수 있습니다. 메모리에 올려진 배열은 당연히 Random Access가 되니까요. 

그런데 만일 데이터의 크기가 수백 MB나 수 GB가 넘어가면, 모든 파일의 내용을 읽어 들이는 것이 비효율적입니다. 시스템 사양에 따라 불가능할 수도 있구요. 이런 경우, ASSOC 함수를 이용해서 파일을 열어 두고, 파일 안을 자유롭게 왔다 갔다 하며 필요한 부분만 뽑아낼 수 있어야 합니다. 거대한 데이터베이스 파일들은 모두 이러한 파일 구조를 기본으로 사용합니다. 그럴 수밖에 없습니다. 누구 한 사람의 주소를 조회하기 위해서 전국민의 데이터베이스를 메모리에 올릴 필요는 없으니까요. Binary 파일의 Random Access 가능한 장점을 활용하기 위해서 ASSOC 함수, 이름만 기억해 두시면 언젠가 쓸모가 있을 것이라고 생각합니다. 

 

0 0