본문 바로가기
프로그래밍/Python

파이썬으로 파일 이진수로 변환하기

by 페이지다운 2020. 2. 15.
반응형

우리가 컴퓨터에 관한 얘기를 듣다 보면 컴퓨터는 0과 1로 이루어져있단 소리를 듣곤 한다.

 

그러나 우리는 한번도 컴퓨터가 표시한 0과 1을 직접적으로 본 적은 없다.

 

궁금하지 않은가? 그럼 한번 보도록 하자.

 

기본적으로 모든 파일은 0과 1로만 이뤄진다. 이걸 우리가 열어봤을때 컴퓨터는 특정한 방식으로 이 0과 1의 조합을 읽어들이고, 표시한다.

 

내가 쓰고 있는 이 글도 0과 1의 조합일 뿐이지만 이를 특별한 규칙에 따라 읽으면 우리가 읽을 수 있는 글이 된다.

 

대표적인 문자 인코딩은 유니코드다. 원래는 영어와 일부 특수문자만 읽을 수 있는 아스키가 있었지만, 더욱 많은 문자의 표현이 필요해지면서 나타났다.

 

이는 사진, 영상, 음악과 같은 문자로 이루어진게 아닌 파일에도 똑같다. 이런 파일을 그냥 다짜고짜 메모장으로 열어보자.

 

구글 로고 사진을 메모장으로 연 모습

알 수 없다. 이는 문자를 표현하는 규칙에 따른 파일이 아니기 때문에 문자를 읽는 규칙에 따라 읽으면 알아볼 수가 없다.

 

이 사진은 png 사진이므로 png 사진을 읽는 규칙에 따라 읽어야 한다. 그럼 우리는 사진을 볼 수 있다.

 

이렇게 파일을 어떤 방식으로 읽어야 하는지 등을 알려주는 파일의 첫부분을 헤더라고 한다.

 

그럼 바로 시작해 보자.

 

일단 파이썬으로 파일을 읽으면 바이트 자료형으로 읽어들인다. 우리는 0과 1로 이루어진 문자열로 바꿔야 한다.

 

1바이트는 8비트이므로 1바이트당 8개의 0과 1 조합으로 표시하면 된다. 코드를 보자.

 

1
2
def byteToBit(d):
    return ''.join('{:08b}'.format(b) for b in d)
cs

 

그렇다. for 문을 통해 1바이트씩 이진수로 변환해 하나로 합친다.

 

이미지 파일을 불러온 다음 다시 이진수로 이루어진 텍스트 파일을 만드는 코드를 작성해 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
def bitToByte(d):
    return int(d, 2).to_bytes(len(d) // 8, byteorder='big')
 
= open('image.png''rb')
data = f.read()
data = byteToBit(data)
f.close()
 
= open('bintext.txt''wt')
f.write(data)
f.close()
 
cs

 

이러하다. 그럼 아까 그 구글 로고 사진을 재활용해 보자.

 

원본 사진

 

일부분

원본 사진은 5969바이트이다.

 

그러면 바이트 하나에 8비트이므로 이 텍스트의 총 길이는 5969*8 = 47752이 된다.

 

텍스트 파일의 크기를 보자. 47752바이트인 것을 알 수 있다. 왜일까?

 

여기서의 0과 1은 컴퓨터에서 다루는 진짜 0과 1이 아니다. 텍스트 인코딩(유니코드)의 0과 1이다. 즉, 문자열이다.

 

유니코드에서 숫자는 하나에 1바이트의 공간을 차지하므로 47752바이트가 맞다.

 

근데 이 텍스트 파일에서 다시 이미지를 만들어낼 수 있을까?

 

당연히 된다.

 

다음은 이진수에서 바이트로 바꿔주는 코드다.

 

1
2
def bitToByte(d):
    return int(d, 2).to_bytes(len(d) // 8, byteorder='big')
cs

 

아까와는 좀 다르다.

 

코드를 완성시켜 보자.

 

1
2
3
4
5
6
7
8
9
10
11
def bitToByte(d):
    return int(d, 2).to_bytes(len(d) // 8, byteorder='big')
 
= open('bintext.txt''rt')
data = f.read()
data = bitToByte(data)
f.close()
 
= open('image.png''wb')
f.write(data)
f.close()
cs

 

이번에는 반대다.

 

실행시키면 정상적으로 파일이 돌아온 것을 알 수 있다.

 

자 이렇게 우리는 파일을 이진수로 변환시켜 볼 수 있었다. 이거 말고도 바이너리 파일을 문자열로 다루고 싶은 사람들을 위해 번외로 방법을 알려드리겠다.

 

base64 인코딩을 이용하면 어떤 바이트든 문자열로 바꿀 수 있다.

 

요놈은 애초에 사람이 읽을 수 있도록 설계된 인코딩이 아니라 문자열 암호화 같은 곳에서 문자열을 이용해야 하는데 값이 일반적인 문자열 인코딩으로 변환할 수 없을 때를 위한 인코딩이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import base64
 
= open('image.png''rb')
data = f.read()
data = base64.b64encode(data).decode('utf-8')
f.close()
 
= open('b64text.txt''wb')
f.write(data)
f.close()
 
= open('b64text.txt''rt')
data = f.read()
data = base64.b64decode(data)
f.close()
 
= open('image1.png''wb')
f.write(data)
f.close()
cs

 

(중간에 유니코드로 디코딩하는건 텍스트파일로 써야 하는데 b64encode 함수가 인코딩된 유니코드를 내놓기 때문이다)

 

실행해 보면

이렇게 문자열로 변환되는 모습을 볼 수 있다.

반응형

댓글