파일에는 텍스트파일과 이진파일이 있다.
CPU의 입장에서 보면 두 파일 모두 0과 1의 조합으로 이루어져 있다. 그런데 왜 텍스트 파일과 이진파일로 구분하느냐?
텍스트파일은 아스키코드나 유니코드 텍스트 파일로 사람이 손쉽게 열람할 수 있다. 컴퓨터에게는 숫자의 연속이기 때문에 별 차이가 없지만 사람에게는 중요하다. 손쉽게 열람할 수 있다는 것은 조작할 수 있다는 말과 같다.
스크립트나 소스코드가 왜 텍스트 파일로 이루어져 있는가를 생각하면 이해가 간다. 키보드로 글자를 몇개 타이핑 하는 것만으로도 컴퓨터를 제어할 수 있다는 것은 인간에게 큰 메리트이다. 컴퓨터는 스스로의 시스템으로 움직이지만 명령을 하는 것은 인간이다. 인간이 컴퓨터를 제어하는데 있어서 텍스트 파일은 매우 중요하다.
물론 텍스트 파일을 사용하여 사람들간의 통신을 하는 것도 오늘날 대단히 중요하다. html 파일 같은 것들도 텍스트 파일이다. 텍스트 파일이냐 아니냐를 알기 위한 방법은 간단하다. 윈도우즈의 메모장으로 열어서 글자가 제대로 나오면 텍스트 파일이다. 파이썬 파일은 메모장으로 열면 소스코드가 보인다.
반면 이진파일은 해독이 불가능하다. 뭔가 이상한 기호들이 듬성듬성 나열되있다. 공백이 있다고 해서 데이터가 없는게 아니다. 메모장에서 해독이 불가한 코드는 글자로 보여지지 않는다.
다른 운영체제에서도 마찬가지다. 텍스트 에디터로 파일을 열어보면 이진 파일은 이상한 기호가 나열되 있다. 즉 텍스트 파일은 아스키 코드나 유니 코드인가를 메모장 프로그램에서 파악하여 그 형식에 따라 문자로 출력하는 것이다. 실제 프로그램의 저장은 숫자로 되어 있다. 메모장은 코드를 해석해서 코드에 맞는 폰트를 화면에 출력할 뿐이다.
아스키코드에 대한 설명은 아래 포스팅에 있다. 아스키 코드는 워낙 컴퓨터공학의 기초에서 다루기 때문에 관련 자료가 온라인에 많이 나와있다. 위키백과 등 몇개의 포스팅을 읽어보면 어렵지 않게 이해할 수 있다. 보통 아스키코드를 볼때 이진법도 같이 배운다. 둘은 연관된 내용이니 같이 보면 좋다.
파이썬에서 텍스트 파일을 사용하는 것은 간단하다.
file = open('mytextfile.txt','w')
file.write('Time is short you need Python.')
file.close()
이렇게 코드를 실행하면 텍스트 파일이 생성되고 메모장에서 열어보면 file.write 에 넣은 문자열이 저장되어있다.
OKAY 그러면 이진파일,텍스트 파일 둘 중 하나의 파일에 쓰는 문제는 해결되었다. 그러면 이진파일은 어떻게 저장해야 하는가?
PC안에 있는 파일의 비율을 보면 텍스트파일보다 이진파일이 많다. 운영체제를 동작하는 커널과 입출력 장치를 제어하는 드라이버들 시스템 유틸리티 등 컴퓨터를 동작시키는 것은 모두 이진파일이다. 굳이 텍스트 파일과 비교하면 텍스트 파일은 인간이 읽을 수 있는 정보 - 인간끼리 소통이 가능한 정보 - 이자 컴퓨터를 조작하기 위한 언어규칙 -어떤 컴파일로로 해석되느냐가 언어의 종류가 결정됨 - 이다. 텍스트 파일이 적음에도 중요한 이유는 인간끼리 서로 소통해서 컴퓨터를 조작하고 컨트롤 하기 위해서이다. (전자적인 지배라고 이야기 할 수도 있다)
애초에 목적이 다르기 때문에 CPU는 텍스트 파일을 해독할 능력이 떨어진다. 그렇기 때문에 C나 자바 파이썬 같은 프로그래밍 언어가 존재한다. 텍스트로 된 소스코드를 기계에서 해석하는 방식이 컴파일이건 인터프리팅이건 그 과정을 거쳐서 기계어 코드가 나와야 한다. 그렇지 않으면 CPU는 단 한줄의 코드도 실행시킬 수 없다.
인텔 계열 CPU라면 기계어로 제작된 Intel Instruction Set 이 있다. 텍스트로 된 소스코드와 다르게 이 명령어를 전달하면 CPU가 바로 알아들을 수 있다. 컴파일러나 인터프리터가 필요없다.
0x00은 16진수 표시다. 16진수는 2진수로 변환하기가 쉽다. 결국 2진수의 명령어와 데이터들이 모여있는 것이 이진파일이다. 이진파일은 2진파일이다. 용어에 헷살리면 안된다. 일상생활에서는 숫자 2를 이라고 표시하지 않아서 혼동에 주의한다.
이진파일은 텍스트파일처럼 인간이 읽지 않기 때문에 (보통 사람은 안본다) 오로지 CPU와 기계에 최적화 되어 있다. Instruction set을 해독할 수 있으면 소스코드를 컴파일하지 않아도 CPU에 직접 명령을 내릴 수 있다. 그것도 빠른 속도로. 그러나 인간에게 이진파일을 작성해서 컴퓨터를 조작하라는 것은 비효율적이다.
지금은 상상하기 힘들지만 그런 시절도 있었다. 1900년대 중반의 천공카드는 직접 0과1의 명령어를 기계에 입력시켰다.
컴퓨터 시스템의 근본을 이해하는데 있어서 가끔 과거를 돌아보는 것도 필요하다. 100년전 천공카드를 사용하는 사람들을 우리가 미개하다고 볼 수 있을까? 그 당시의 사회에는 IT혁명같은 일이었고 사람들에게 영감을 불어넣었는데 우리가 우습게 볼 수 있을까? 아니 천공카드의 발명이 없었다면 지금의 이진법 컴퓨터 기술이 존재했을까? 라는 질문은 흡사 닭이 먼저냐 알이 먼저냐라는 풀리지 않는 문제같다.
약간은 오버스럽지만 과거를 보면 미래도 보인다고 생각한다. 100년전 컴퓨팅 환경을 보면 100년 후에 우리를 미개하다고 생각할지 모를 인류의 후손들이 떠오른다. 100년 전을 비웃었다면 100년 후를 예상할 정도의 자신감을 가져도 좋다. 천공카드처럼 언젠가 낙후될 지금의 기술을 맹신하지는 않는다. 다만 철저하게 과거부터 미래까지 컴퓨팅 환경의 근본 원리를 추구하는 것은 좋다. 발전적이다.
파일에 대한 이야기는 이 정도로 하고 이진파일을 저장하는 법을 알아본다. 파이썬으로 텍스트 파일을 저장하는 것은 위에서 본 것 처럼 딱히 학습할 필요가 없다.
이진 파일을 저장하는 모듈 또한 다양하다. 왜냐하면 프로그램의 목적에 따라 달라질 것이기 때문이다. 컴파일한 코드 자체가 이진 파일이다. 파이썬은 인터프리팅 방식으로 소스코드를 즉석에서 기계어로 번역하여 실행하지만 따지고 보면 결국 컴파일을 하는 것과 결과는 비슷하다. 컴파일 형식으로 한꺼번에 모아서 하느냐, 인터프리팅 방식으로 런타임에서 즉석으로 진행하느냐. 어쨋든 CPU가 알아듣도록 소스코드를 바꿔야한다.
이 포스팅에서는 파이썬의 변수 (변수,리스트,튜플)를 이진 파일에 저장하는 모듈을 사용한다. 이름이 shelve 인 것은 마치 선반처럼 변수를 쌓아둔다는 의미이다.
import shelve
shelffile = shelve.open('storevar')
list1 = [1,3,5,7,9]
shelffile['l1'] = list1
shelffile.close()
shelffile = shelve.open('storevar')
list2 = [2,4,6,8,10]
shelffile['l2'] = list2
shelffile.close()
shelffile = shelve.open('storevar')
string1 = "Hello My friend"
shelffile['s1'] = string1
shelffile.close()
shelffile = shelve.open('storevar')
myNumber1 = 99999
shelffile['n1'] = myNumber1
shelffile.close()
shelffile = shelve.open('storevar')
myFloat = 233.54
shelffile['f1'] = myFloat
shelffile.close()
shelffile = shelve.open('storevar')
myTuple = ("tuple", 23, "mixed", 100.5, "variable")
shelffile['t1'] = myTuple
shelffile.close()
예제에서는 리스트와 문자열, 정수형,실수형, 튜플형을 이진 파일에 저장한다.
인수 'storevar' 는 파일의 이름이다. 파일을 오픈하면 세개의 파일이 생성된다.(윈도우 10기준)
* 저장하려는 리스트나 문자열 등을 정의하여 shelffile 객체에 담는다. [ ] 안에는 key값을 입력한다. 변수를 꺼내올 때 key 값으로 대응하는 value를 꺼내온다. 파이썬의 딕셔너리(사전형) 사용과 비슷하다.
파일에 쓰기가 끝나면 close 로 닫아준다. 가급적 파일이 열린 상태를 오래두지 않도록 한다. 항상 다른 프로세스나 스레드가 같은 파일을 참조할 가능성이 있다. 지금은 멀티 스레드가 아니지만 파일을 빨리 닫아주는 것이 좋다.
운영체제 입장에서 보자. 파일이 열려있다는 것은 어떤 프로세스가 이 파일의 사용권을 점유하고 있고 파일을 조작하는 포인터가 열려있다는 말이다. 운영체제는 여러 프로세스에서 하나의 파일에 동시에 접근하도록 놔둘수가 없다. 프로그래머는 누가 이 파일을 사용하러 올지 알 수 없다. 그래서 파일을 열고 용무가 끝나면 바로 닫으라고 하는 것이다.
이제 이진 파일에 저장되었으니 프로그램 종료 후에도 변수들이 파일에 저장되어 있다. 변수들은 프로그램이 종료하는 순간 메모리에서 해제된다. 정상적으로 프로그램이 종료하지 않아도 일단 종료되면 그 변수들을 찾을 방법이 없다. 그러나 중간에 파일에 저장하면 다음 실행시에 데이터를 불러올 수 있다.
다음 코드는 이진파일에서 저장된 변수들을 불러온다. 다양한 방법으로 변수들을 불러올 수 있음을 확인한다.
import shelve
shelfFile = shelve.open('storevar')
print("------------------------------------------------")
print("class type : ", type(shelfFile))
print("sheleFile['l1'] : ",shelfFile['l1'])
print()
for v,g in list(shelfFile.items()):
print("[key] :", v, "[values] :",g)
print()
print(list(shelfFile.keys()))
print(list(shelfFile.values()))
shelfFile.close()
최초 저장할 때 key를 넣은 이유다. key를 사용하면 매핑되는 value 가 따라 나온다. 클래스는 다르지만 딕셔너리형과 같다. items() keys() values() 는 딕셔너리에서 요소를 가져오기 위해 사용하는 메소드이다.
역시 파이썬 답게 매우 쉽게 저장하고 불러올 수 있다.
아스키코드나 유니코드 정도로 해석이 되는 텍스트파일과는 다르게 이진파일은 전부 제각각의 포맷이 되버리는 이유를 알 수 있을 것이다. 이 코드처럼 보통의 일관성이 없이 저장된다. 리스트를 저장하고 싶으면 저장하고, 숫자를 저장하고 싶으면 문자열 뒤에 넣고... 이러다 보니 이 파일을 사용하는 프로그램 이외에는 해석이 어렵다.
사람이 읽기 어렵기 때문에 해당 포맷에 전문가가 아니면 알기 어렵다는 문제도 있다.
기능만 따지면 단순히 파이썬 자료형 몇개를 저장해놓은 파일이지만 이진파일의 성격에 대하여 좀 더 다루어봤다.