IDL/Programming

텍스트 파일을 효과적으로 읽는 방법에 관하여 [3]

이상우_IDL 2014. 5. 16. 22:47
728x90

지난 회 게시물에서 예고해드린대로 오늘은 아래와 같은 텍스트 파일을 READCOL이 아닌 IDL 자체 내장 기능을 사용해서 읽는 방법을 중심으로 적어보겠습니다. 아래 보시는 바와 같이, 실제 자료값들이 존재하는 두번째 줄부터는 각 줄마다 컬럼을 구분하는 분리자가 있는 상태로 가정합니다. 여기서는 코마(,)가 분리자로 사용되고 있습니다.

 

a b c d e
1, 1, 1, 1,
2, 2, 2, , 
3, 3, 3, 3, 3
4, 4, 4, ,
5, 5, 5, 5,


이와 같은 내용을 담은 파일이 'aaa.txt'라고 가정하면 이런 자료파일을 읽기 위한 프로그램은 기본적으로 다음과 같은 내용이면 충분합니다. 그리고 이런 내용의 작업은 커맨드 입력창에서 한 줄씩 실행하기보다는 하나의 프로시저 형태로 만들어서 컴파일 및 실행을 하는 것이 좋습니다. 따라서 아래의 내용을 프로시저 이름을 그대로 따서 test_text.pro로 저장하고 컴파일 및 실행을 권합니다.

 

PRO test_text

file = 'aaa.txt'

OPENR, lun, file, /GET_LUN; 채널 열기

ss = ''

READF, lun, ss; 헤더 한 줄 읽고 넘기기

WHILE ~EOF(lun) DO BEGIN; WHILE문 시작

  READF, lun, ss; 한 줄의 내용을 통째로 읽어 ss라는 문자변수에 저장

  spl = STRSPLIT(ss, ',', /EXTRACT, /PRESERVE_NULL); ss를 분리자인 코마(,)를 단위로 분할하여 분할된 문자값들을 배열로 저장

  HELP, spl; 한 줄의 내용이 분리자로 분할되어 생성된 문자배열 spl의 형태 확인

ENDWHILE; WHILE문 종료

FREE_LUN, lun; 채널 닫기

END

 

여기서는 헤더 한 줄을 그냥 읽고 넘긴 다음 줄부터가 본격적인 자료값들의 시작입니다.물론 이 파일의 경우는 자료값들이 있는 줄 수는 5개인 것이 눈으로 빤히 보입니다. 그런데 만약 헤더를 제외한 나머지 부분의 줄 수의 갯수를 굳이 확인하지 않고, 그냥 파일의 내용이 끝날 때까지 읽는 작업을 반복하는 것이 좋을 수도 있습니다. 이럴 경우에는 위와 같이 WHILE문을 사용해서 EOF(lun)의 값이 0이 아닐 때까지 반복하도록 하면 됩니다. 참고로 EOF(lun)의 의미는 lun이라는 채널을 통해 있는 파일의 내용이 끝부분에 다다랐는가를 EOF(End Of File)라는 내장함수로 체크하라는 것으로, 파일의 끝에 다다르면 1이고 아직 끝이 아니면 0이 됩니다. 따라서 WHILE 바로 뒤에 있는 ~EOF(lun)이 1인 경우(즉, 아직 끝이 아니면)에는 반복이 계속 됩니다. 물론 1이면, 즉 파일 끝에 다다랐다면 반복이 종료됩니다. 그래서 WHILE문을 이런 방식으로 활용하는 것도 매우 효과적입니다.

 

물론 파일의 전체 줄 수를 FILE_LINES 함수로 파악한 후, 이 줄 수에서 헤더의 줄 수를 빼면 그 수가 결국 자료값들의 줄 수가 됩니다. 따라서 이렇게 줄 수롤 구체적으로 파악해서 반복의 횟수를 직접 제어하는 방식으로 프로그램을 작성하는 것도 가능합니다. 물론 이럴 경우에는 WHILE문 대신 FOR문을 사용하면 됩니다. 만약 이러한 방식을 사용할 경우에는 위의 프로그램의 내용은 아래와 같이 바꿀 수도 있습니다. 물론 위와 아래의 프로그램은 중간의 내용만 다를 뿐 모두 동일한 역할을 수행합니다.

 

PRO test_text

file = 'aaa.txt'

nl = FILE_LINES(file); 파일의 전체 줄 수를 nl이라는 변수에 저장

OPENR, lun, file, /GET_LUN; 채널 열기

ss = ''

READF, lun, ss; 헤더 한 줄 읽고 넘기기

n = nl - 1; 파일 전체 줄 수에서 헤더의 줄 수를 빼면 실제 자료값들의 줄 수가 나오며 이를 n이란 변수에 저장

FOR i = 0, n-1 DO BEGIN; FOR문 시작

  READF, lun, ss; 한 줄의 내용을 통째로 읽어 ss라는 문자변수에 저장

  spl = STRSPLIT(ss, ',', /EXTRACT, /PRESERVE_NULL); ss를 분리자인 코마(,)를 단위로 분할하여 분할된 문자값들을 배열로 저장

  HELP, spl; 한 줄의 내용이 분리자로 분할되어 생성된 문자배열 spl의 형태 확인

ENDFOR; FOR문 종료

FREE_LUN, lun; 채널 닫기

END

 

이제 반복의 과정속에서 HELP 명령에 의하여 출력되는 spl 배열의 구조에 주목해봅시다. 위의 프로그램(둘 중 어느 것이든)을 실행하면 아마 다음과 같은 결과가 출력될겁니다.

 

SPL             STRING    = Array[5]
SPL             STRING    = Array[5]
SPL             STRING    = Array[5]
SPL             STRING    = Array[5]
SPL             STRING    = Array[5]

이 결과의 의미는 각 줄마다 코마(,)라는 분리자로 분리된 컬럼 수가 5개이며, 분할된 내용들은 문자의 형태로 spl이라는 배열에 저장되어 있다는 의미입니다. 물론 이러한 작업은 반복문에 의하여 매 줄 단위로 수행됩니다. 이러한 분할 작업에 있어서는 STRSPLIT라는 내장함수가 사용되었습니다. 여기서는 ss라는 변수에 들어가 있는 문자값을(한 줄의 내용 전체) 분리자인 ','를 단위로 분할하여 배열로 저장하라는 의미인데요. 여기서 /PRESERVE_NULL이라는 키워드가 사용되었는데요. 이 키워드의 의미는 지난 회 게시물에서 살짝 언급된 바 있습니다. 분리자 사이에 아무런 값이 없을 경우 null이라는 형태로라도 하나의 값으로 인식하라는 의미입니다. 특히나 이번 자료와 같이 분리자 사이에 아무 값도 없는 경우들이 있다면 이 키워드의 사용이 반드시 필요합니다.

 

그리고 만약 각 줄마다 인식된 spl의 구성원 값들을 직접 확인하고 싶다면, HELP문 바로 다음에 PRINT, spl과 같은 문구를 넣어도 됩니다. 이렇게 PRINT문을 추가한 상태로 프로그램을 다시 돌린다면 출력 결과는 아래와 같을 것입니다.

 

SPL             STRING    = Array[5]
1  1  1  1
SPL             STRING    = Array[5]
2  2  2   
SPL             STRING    = Array[5]
3  3  3  3  3
SPL             STRING    = Array[5]
4  4  4  
SPL             STRING    = Array[5]
5  5  5  5

여기서는 분리자는 이미 제외된 상태로 자료값들 자체만 spl 배열내에 들어간 상태로 출력이 되어 있습니다. 물론 줄에 따라서 값이 3~5개로 그 갯수가 다른데요. 실제로는 매 줄마다 5개의 값들이 다 출력된 상태이지만, 값들 중에 null에 해당되는 것도 출력이 된 상태입니다. 물론 이러한 null은 출력이 된다고 해도 실제로는 보이지 않습니다. 어쨌든 매 줄마다 5개씩의 값들을 인식한 상태입니다.

 

* 작성하다보니 내용이 길어져서 나머지 내용은 바로 뒤 4회차 게시물로 넘기겠습니다.

 

LIST