IDL/Programming

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

이상우_idl 2020. 4. 7. 15:36
728x90
반응형

제가 예전에 동일한 제목으로 총 4개의 게시물들을 시리즈로 올린 적이 있습니다. 저는 사실 얼마 안되었다고 생각했는데, 네번째 게시물을 올렸던 때를 찾아보니 2014년 5월로 벌써 약 6년 전이었네요. 시간 참 빠릅니다. 하여간 이번에 동일한 제목으로 다섯번째 게시물을 올리게 되었습니다. 사실 텍스트 파일이라는 것이 우리가 외부로부터 데이터를 얻는 방식들 중 꽤 흔한 편에 속합니다. 그렇기 때문에 파일 내에 데이터가 수록된 형태에 있어서 다양한 케이스들이 있습니다. 물론 그런 모든 경우들을 제가 여기서 다 다뤄볼 수는 없겠지만, 뭔가 주목해볼만한 케이스라고 판단된다면 최대한 그 관련 사례를 여기서 소개해볼 생각입니다. 오늘 제 5편에서 소개할 예제는 기존에 제가 1~4편에서 다뤘던 예제들과는 약간 다른 케이스인데요. 우선 예제 텍스트 파일을 게시물에 첨부하였습니다. 여러분들도 일단 이 파일을 받아서 내용을 한번 보시기 바랍니다.


test_sample.txt


이 텍스트 파일에 수록된 내용을 여기에도 옮겨보면 다음과 같습니다.


4.6 2.7 8.4 5.9 7.1

4.6          2.7          8.4          5.9          7.1

4.6 2.7 8.4 5.9 7.1


이와 같이 총 3개의 라인들로 구성되어 있는데, 사실 각 라인의 내용은 서로 거의 동일합니다. 각 라인별로 5개의 실수값들이 적혀있습니다. 적혀있는 숫자들 자체는 동일한데, 차이가 나는 것은 값 사이사이의 분할 방식입니다. 첫번째 라인의 경우는 공백(Space) 1칸이고, 두번째 라인의 경우는 공백 10칸입니다. 세번째 라인의 경우는 탭(Tab)입니다. 그러면 이러한 내용이 수록된 텍스트 파일을 IDL에서 읽는 작업을 해보도록 하겠습니다. 세 라인들을 각각 읽어나가면서 처리 방법을 살펴봅시다. 이 작업을 위한 예제 코드는 다음과 같습니다.


file = 'test_sample.txt'

s1 = ''

s2 = ''

s3 = ''

OPENR, lun, file, /GET_LUN

READF, lun, s1

HELP, s1

READF, lun, s2

HELP, s2

READF, lun, s3

HELP, s3

FREE_LUN, lun


이 예제 코드에서는 OPENR 명령을 사용하여 IDL과 파일 사이의 연결통로를 열어두고, READF 명령을 사용하여 순차적으로 각 라인의 내용을 문자값으로 읽어서 각각 문자형 변수 s1, s2, s3에 담았습니다. 그리고 읽는 작업이 모두 끝난 후에는 FREE_LUN 명령을 사용하여 IDL과 파일 사이의 연결통로를 닫았습니다. HELP에 의하여 출력된 내용을 보면 s1, s2, s3이 제대로 내용을 담고 있는지 확인이 가능합니다. 이제 각 라인별로 수록된 5개의 값들을 실수형 배열로 묶어보고자 합니다. 이 작업을 위해서는 제가 이전 게시물들에서도 소개했던 STRSPLIT 함수를 사용합니다. 먼저 첫번째 라인의 내용을 문자값으로 담고 있는 s1에 대하여 다음과 같이 작업합니다.


spl = STRSPLIT(s1, ' ', /EXTRACT)

HELP, spl

PRINT, spl


여기서는 s1에 수록된 문자값 내용을 1칸의 공백 ' '으로 분할 추출하여 배열 spl에 담게 됩니다. 이렇게 얻어진 배열 spl에 대한 정보를 확인하기 위하여 HELP 및 PRINT 명령을 사용하여 그 결과를 출력하도록 하였습니다. 출력된 결과는 다음과 같습니다.


SPL             STRING    = Array[5]

4.6 2.7 8.4 5.9 7.1


이 내용을 보면 spl은 분할된 5개의 문자값들로 구성된 문자형 배열임을 확인할 수 있습니다. 분할만 되었을 뿐이므로 배열 spl을 구성하는 5개의 값들 각각은 아직 문자형 값입니다. 실수형 값들로 구성된 배열 데이터로 변환하려면 다음과 같이 자료형 변환 함수 FLOAT를 사용하면 됩니다.


data = FLOAT(spl)

HELP, data

PRINT, data


배열 data에 대하여 출력된 내용은 다음과 같습니다.


DATA            FLOAT     = Array[5]

      4.60000      2.70000      8.40000      5.90000      7.10000


이 결과를 보면 우리가 의도했던대로 5개의 실수형 값들로 구성된 배열 데이터를 얻었음을 알 수 있습니다. 일단 첫번째 라인의 내용을 해독하여 처리하는 작업은 완료되었습니다.


그러면 이어서 두번째 라인의 내용을 처리해봅시다. 이 내용은 변수 s2에 담겨져 있습니다. 사실 전반적인 흐름은 앞서 s1을 처리했던 과정과 거의 같습니다. 다만 차이점은 s1의 경우는 값 사이에 1칸 짜리 공백이 있었던 반면, s2의 경우는 값 사이에 10칸 짜리 공백이 있다는 것입니다. 즉 공백의 크기가 다릅니다. 이런 경우에는 처리 과정에 있어서 어떤 차이를 두어야 할까요? 결론을 말씀드리면 처리 방법상으로는 아무런 차이가 없습니다. 즉 위의 처리 과정에서 s1 대신 s2로 바꿔주는 것 외에는 전혀 차이가 없습니다. 실제로 그렇게 처리를 해봅시다.


spl = STRSPLIT(s2, ' ', /EXTRACT)

HELP, spl

PRINT, spl

data = FLOAT(spl)

HELP, data

PRINT, data


이 과정에 의하여 출력된 내용들을 모두 보면 다음과 같습니다.


SPL             STRING    = Array[5]

4.6 2.7 8.4 5.9 7.1

DATA            FLOAT     = Array[5]

      4.60000      2.70000      8.40000      5.90000      7.10000


이와 같이 앞서 s1에 대하여 처리했을 때와 결과는 동일합니다. 즉 이것은 값 사이의 공백이 단 1칸이든 10칸이든 상관없이 STRSPLIT 함수에 의하여 분할될 때에는 차이가 없다는 얘기입니다. 값 사이의 공백의 칸 수가 일정하지 않아도 상관없습니다. 즉 첫 공백은 10칸이고 두번째 공백은 8칸이고 이런 식으로 서로 달라도 상관이 없다는 뜻입니다. STRSPLIT 함수로 분할 추출하는데 있어서는 사이 공백의 칸 수는 무조건 1칸 또는 그 이상이면 다 동일하게 취급된다고 보면 됩니다.


그러면 이번에는 세번째 라인의 내용을 처리해봅시다. 이 내용은 변수 s3에 담겨져 있습니다. 앞서 s1, s2에 대하여 처리했던 과정과 동일한 방식으로 즉 1칸의 공백으로 분할하는 방법을 적용하여 다음과 같이 처리해봅시다.


spl = STRSPLIT(s3, ' ', /EXTRACT)

HELP, spl

PRINT, spl

data = FLOAT(spl)

HELP, data

PRINT, data


이 때 출력되는 결과 내용을 보면 다음과 같습니다.


SPL             STRING    = Array[1]

4.6 2.7 8.4 5.9 7.1

DATA            FLOAT     = Array[1]

      4.60000


그런데 뭔가 좀 이상합니다. 앞서 s1, s2에 대한 결과와는 전혀 다릅니다. STRSPLIT 함수의 결과로 얻어진 spl은 배열의 형태를 띄고 있긴 하지만 구성원 값이 단 하나인 배열입니다. 5개가 아닙니다. 물론 spl을 출력한 내용을 보면 5개의 값들이 보이긴 하지만, 이 모습은 변수 s3의 내용과 동일한 한 덩어리의 문자값일 뿐입니다. 그러므로 배열 spl을 실수형으로 변환한 배열인 data에는 맨 앞의 값인 4.6 하나만 담겨지고 나머지 값들은 전혀 반영되지 않았습니다. 대체 어디서부터 잘못되었을까요?


근본적인 원인은 s3를 분할하기 위하여 STRSPLIT 함수를 사용하는데 있어서 공백(Space)은 제 역할을 못한다는 것입니다. 앞서 제가 언급했듯이 세번째 라인의 경우는 값 사이가 공백이 아닌 탭(Tab)으로 분할되어 있습니다. 따라서 STRSPLIT 함수를 사용할 때에도 분할자로서 공백 대신 탭이 사용되어야 합니다. 그러면 STRSPLIT 함수를 사용할 때 이 탭(Tab)을 어떻게 명시해야 할까요? 일단 키보드 상의 탭 키로 입력을 하는 방법을 생각해볼 수 있습니다.


spl = STRSPLIT(s3, '     ', /EXTRACT)


즉 따옴표 사이에서 직접 키보드의 탭 키로 입력을 하는 것인데요. 그러나 이 방법은 키보드의 종류나 OS 등에 따라서는 제대로 먹히지 않을 수도 있습니다. 그래서 이 방법은 권장하지 않습니다. 그 대신에 탭(Tab) 입력에 해당되는 아스키(ASCII) 코드를 직접 명시하는 방법을 권장합니다. 다음과 같이 해당 아스키 코드 번호인 9를 STRING 함수를 이용하여 명시하면 됩니다.


spl = STRSPLIT(s3, STRING(9B), /EXTRACT)


이 때 코드 번호 9는 그냥 정수로 적으면 안되고 반드시 바이트(Byte)형 값으로 적어줘야 하기 때문에 이와 같이 숫자 9 바로 뒤에 알파벳 B를 붙여야 합니다. 이 부분을 제외하면 나머지 과정은 그대로입니다.


HELP, spl

PRINT, spl

data = FLOAT(spl)

HELP, data

PRINT, data


출력된 결과는 다음과 같습니다.


SPL             STRING    = Array[5]

4.6 2.7 8.4 5.9 7.1

DATA            FLOAT     = Array[5]

      4.60000      2.70000      8.40000      5.90000      7.10000


이 결과는 앞서 s1, s2에 대하여 얻었던 것과 동일합니다. 따라서 탭(Tab)을 기준으로 제대로 분할되어서 작업이 진행되었음을 알 수 있습니다. 이와 같이 한 라인상에서 값 사이사이가 공백(Space)이 아닌 탭(Tab)으로 나눠진 경우들이 실제로도 종종 있습니다. 그래서 텍스트 파일의 형태로 주어진 데이터를 IDL에서 읽고자 할 때 이러한 부분까지 신경을 써야 할 수도 있습니다. 따라서 읽고자 하는 텍스트 파일이 있을 때, 내부적으로 값들이 어떤 형태로 수록되어 있는가에 대해서는 처리에 앞서서 한번쯤 직접 점검해보는 것도 중요합니다.


제가 앞서서 언급했듯이, 텍스트 파일에 값들이 수록된 형태는 생각보다 매우 다양합니다. 그래서 제가 1~4편 및 오늘 소개해드린 내용을 바탕으로 하여 분석자가 나름의 창의적 방법을 고안하여 적용해야 할 수도 있습니다. 앞으로도 혹시 주목해볼만한 케이스가 또 발견된다면 저도 이 자리를 통해서 그 사례를 소개해보도록 하겠습니다.

test_sample.txt
0.0MB
반응형