2022년 9월 25일 일요일

python read binary file (binary 모드로 파일 열기)

 

바이너리 파일은 일반 텍스트 파일이 아닌 파일입니다.

예: 이미지 파일. 이러한 파일은 컴퓨터 하드 디스크에 일련의 바이트 형태로 저장됩니다. 

이러한 유형의 바이너리 파일은 일반 모드에서 열 수 없으며 텍스트로 읽을 수 없습니다.


1. 준비 단계

python에서 이러한 binary data를 읽을 때 사용하는 유용한 예제를 준비하였습니다.

시작하기 전에 파일을 하나 만들겠습니다. 

가장 기본적인 text 형태로 한글 utf8 형태로 저장하겠습니다.

with open("test.bin", "w", encoding="utf-8") as f:
f.write("[한글]")


2. open을 이용해서 text 파일 읽기

해당 파일을 open을 이용해 한글자씩 읽으면 아래와 같은 형태로 나옵니다.

with open("test.bin", "r", encoding="utf-8") as f:
while True:
data = f.read(1)
if data=='':
break
print(data)

실행 결과

[


]

한글 utf8의 경우 한글자가 1~4byte까지 다양하게 나타날 수 있습니다. ( https://namu.wiki/w/UTF-8 참고)


3. binary 모드

위 예제에서 간단하게 "r" 대신 "rb" 라고 쓰면 간단하게 처리가 가능합니다.

read함수의 경우 인자가 없다면 전체 데이터를 모두 읽어오게 되며 기본적으로는 한글자 read인 경우 byte단위로 리턴이 됩니다.

print('*** binary mode ***')

with open("test.bin", "rb") as f:
while True:
data = f.read(1)
if data==b'':
break
print(data)

결과

b'['
b'\xed'
b'\x95'
b'\x9c'
b'\xea'
b'\xb8'
b'\x80'
b']'


4. byte array에 저장

앞선 예제와 큰 차이는 없습니다.

print('*** binary mode byte array ***')

byte_array = bytearray()
with open("test.bin", "rb") as f:
while True:
data = f.read(1)
if data==b'':
break
byte_array += data
print(byte_array)

결과

bytearray(b'[\xed\x95\x9c\xea\xb8\x80]')


5. byte 한번에 읽기

read할때 인자를 없애는 것만으로도 한번에 읽는것이 가능합니다.

print('*** binary mode read once ***')
# read size는 선택적인 숫자 인자입니다.
# size가 생략되거나 음수면 파일의 내용 전체를 읽어서 돌려줍니다;
byte_array = bytearray()
with open("test.bin", "rb") as f:
data = f.read()
print(len(data))
byte_array = data
print(byte_array)

결과

8
b'[\xed\x95\x9c\xea\xb8\x80]'


6. file pointer

다른 언어에서 많이 사용하는 file pointer라는 개념입니다. 마찬 가지로 python에도 존재합니다.

file pointer라는 것은 다음 읽을 위치를 나타내게 됩니다. 처음에는 0위치에 있다가 한 byte를 읽으면 file pointer 가 1증가 되어서 1이 되게 됩니다. 아래는 예제인데 file pointer의 위치를 알려주는 것이 tell() 이라는것 입니다. 해당값은 "b"(binary) 모드일때만 사용 하도록 합니다.

print('*** binary mode file pointer ***')
with open("test.bin", "rb") as f:
while True:
fp = f.tell()
data = f.read(1)
if data==b'':
break
print(f"fp:{fp},data:{data}")

결과

fp:0,data:b'['
fp:1,data:b'\xed'
fp:2,data:b'\x95'
fp:3,data:b'\x9c'
fp:4,data:b'\xea'
fp:5,data:b'\xb8'
fp:6,data:b'\x80'
fp:7,data:b']'


7. unread

binary의 헤더 분석등을 이용할때 유용한것이 c언어의 getc, ungetc 이라는 것이 있습니다. 여기에서 ungetc는 C언어에서 stream에 문자를 다시 넣는 것을 의미합니다. 이것을 언제 사용하느냐면 한바이트 읽어냈는데 그게 알고 봤더니 다른 형태의 포맷이었다고 한다면 읽어냈던 데이터를 다시 넣어서 분석을 다른 형태로 해야하는 경우에 사용을 합니다.

비슷하게 file pointer를 이용해서 unread라는 것을 만들었습니다. 

def unread(file, count):
file.seek(-count, 1) # 1 seek_cur, 0 seek_first, 2 seek_end

print('*** binary mode unread ***')
with open("test.bin", "rb") as f:
while True:
fp1 = f.tell()
data1 = f.read(1)
fp2 = f.tell()
unread(f, 1)
fp3 = f.tell()
data2 = f.read(1)
if data1==b'' or data2==b'':
break
print(f"fp1:{fp1},data1:{data1},fp2:{fp2},fp3:{fp3},data2:{data2}")

unread에 count는 몇개의 byte를 다시 돌려놓을 것인지 즉 몇바이트 거꾸로 file pointer를 돌려놓을 것인지에 대한 인자입니다.

결과

fp1:0,data1:b'[',fp2:1,fp3:0,data2:b'['
fp1:1,data1:b'\xed',fp2:2,fp3:1,data2:b'\xed'
fp1:2,data1:b'\x95',fp2:3,fp3:2,data2:b'\x95'
fp1:3,data1:b'\x9c',fp2:4,fp3:3,data2:b'\x9c'
fp1:4,data1:b'\xea',fp2:5,fp3:4,data2:b'\xea'
fp1:5,data1:b'\xb8',fp2:6,fp3:5,data2:b'\xb8'
fp1:6,data1:b'\x80',fp2:7,fp3:6,data2:b'\x80'
fp1:7,data1:b']',fp2:8,fp3:7,data2:b']'



댓글 없음:

댓글 쓰기