Programming

이진 파일 읽기 및 각 바이트 반복

procodes 2020. 3. 3. 23:10
반응형

이진 파일 읽기 및 각 바이트 반복


파이썬에서 바이너리 파일을 읽고 해당 파일의 각 바이트를 반복하는 방법은 무엇입니까?


파이썬 2.4 및 이전

f = open("myfile", "rb")
try:
    byte = f.read(1)
    while byte != "":
        # Do stuff with byte.
        byte = f.read(1)
finally:
    f.close()

파이썬 2.5-2.7

with open("myfile", "rb") as f:
    byte = f.read(1)
    while byte != "":
        # Do stuff with byte.
        byte = f.read(1)

with 문은 2.5 이하의 Python 버전에서는 사용할 수 없습니다. v 2.5에서 사용하려면 가져와야합니다.

from __future__ import with_statement

2.6에서는 필요하지 않습니다.

파이썬 3

파이썬 3에서는 약간 다릅니다. 바이트 모드의 스트림에서 더 이상 원시 문자를 가져 오지 않고 바이트 객체를 가져 오므로 조건을 변경해야합니다.

with open("myfile", "rb") as f:
    byte = f.read(1)
    while byte != b"":
        # Do stuff with byte.
        byte = f.read(1)

또는 benhoyt가 말한 것처럼 같지 않은 것을 건너 뛰고 b""거짓으로 평가 되는 사실을 이용하십시오 . 따라서 코드를 변경하지 않고 2.6과 3.x 사이에서 호환됩니다. 바이트 모드에서 텍스트 또는 그 반대로 전환하면 조건을 변경하지 않아도됩니다.

with open("myfile", "rb") as f:
    byte = f.read(1)
    while byte:
        # Do stuff with byte.
        byte = f.read(1)

이 생성기는 파일에서 바이트를 생성하여 파일을 청크로 읽습니다.

def bytes_from_file(filename, chunksize=8192):
    with open(filename, "rb") as f:
        while True:
            chunk = f.read(chunksize)
            if chunk:
                for b in chunk:
                    yield b
            else:
                break

# example:
for b in bytes_from_file('filename'):
    do_stuff_with(b)

반복자생성기 에 대한 정보는 Python 문서를 참조하십시오 .


파일이 너무 커서 메모리에 보관하는 것이 문제가되지 않는 경우 :

bytes_read = open("filename", "rb").read()
for b in bytes_read:
    process_byte(b)

여기서 process_byte는 전달 된 바이트에서 수행하려는 일부 조작을 나타냅니다.

한 번에 청크를 처리하려는 경우 :

file = open("filename", "rb")
try:
    bytes_read = file.read(CHUNKSIZE)
    while bytes_read:
        for b in bytes_read:
            process_byte(b)
        bytes_read = file.read(CHUNKSIZE)
finally:
    file.close()

버퍼링을 무시하고 한 번에 한 바이트 씩 파일을 읽으려면 두 개의 인수 iter(callable, sentinel)내장 함수를 사용할 수 있습니다 .

with open(filename, 'rb') as file:
    for byte in iter(lambda: file.read(1), b''):
        # Do stuff with byte

file.read(1)아무것도 반환하지 않을 때까지 호출 합니다 b''(빈 바이트 열). 대용량 파일의 경우 메모리가 무제한으로 증가하지 않습니다. 당신은 전달할 수 buffering=0open()버퍼링을 사용하지, - 그것은 단지 한 바이트가 반복 (느린) 당 읽도록 보장한다.

with-statement는 아래 코드에서 예외가 발생하는 경우를 포함하여 파일을 자동으로 닫습니다.

기본적으로 내부 버퍼링이 있지만 한 번에 1 바이트를 처리하는 것은 여전히 ​​비효율적입니다. 예를 들어 blackhole.py다음은 제공된 모든 것을 먹는 유틸리티입니다.

#!/usr/bin/env python3
"""Discard all input. `cat > /dev/null` analog."""
import sys
from functools import partial
from collections import deque

chunksize = int(sys.argv[1]) if len(sys.argv) > 1 else (1 << 15)
deque(iter(partial(sys.stdin.detach().read, chunksize), b''), maxlen=0)

예:

$ dd if=/dev/zero bs=1M count=1000 | python3 blackhole.py

그것은 처리 ~ 1.5 GB / s의 경우 chunksize == 32768내 컴퓨터 만에 ~ 7.5 MB / s의 경우 chunksize == 1. 즉, 한 번에 1 바이트를 읽는 것이 200 배 느립니다. 한 번에 둘 이상의 바이트를 사용하도록 처리를 다시 작성할 수 있고 성능이 필요한 경우이를 고려 하십시오.

mmap파일과 bytearray파일 객체를 동시에 취급 할 수 있습니다 . 두 인터페이스에 모두 액세스해야하는 경우 전체 파일을 메모리에로드하는 대신 사용할 수 있습니다. 특히 일반 for루프를 사용하여 메모리 매핑 파일에서 한 번에 한 바이트 씩 반복 할 수 있습니다 .

from mmap import ACCESS_READ, mmap

with open(filename, 'rb', 0) as f, mmap(f.fileno(), 0, access=ACCESS_READ) as s:
    for byte in s: # length is equal to the current file size
        # Do stuff with byte

mmap슬라이스 표기법을 지원합니다. 예를 들어 position에서 시작하는 파일에서 바이트를 mm[i:i+len]반환 len합니다 i. 컨텍스트 관리자 프로토콜은 Python 3.2 이전에는 지원되지 않습니다. mm.close()이 경우 명시 적 으로 호출해야합니다 . 를 사용하여 각 바이트를 반복 mmap하면보다 많은 메모리 소비 file.read(1)하지만 mmap훨씬 빠릅니다.


chrispy, Skurmedel, Ben Hoyt 및 Peter Hansen의 모든 훌륭한 요점을 요약하면 이진 파일을 한 번에 한 바이트 씩 처리하는 최적의 솔루션입니다.

with open("myfile", "rb") as f:
    while True:
        byte = f.read(1)
        if not byte:
            break
        do_stuff_with(ord(byte))

파이썬 버전 2.6 이상의 경우 :

  • 파이썬 버퍼 내부-청크를 읽을 필요가 없습니다.
  • 건조 원리-판독 라인을 반복하지 마십시오
  • with 문은 깨끗한 파일을 닫습니다.
  • 더 이상 바이트가 없으면 'byte'는 false로 평가됩니다 (바이트가 0이 아닌 경우)

또는 속도 향상을 위해 JF Sebastians 솔루션을 사용하십시오.

from functools import partial

with open(filename, 'rb') as file:
    for byte in iter(partial(file.read, 1), b''):
        # Do stuff with byte

또는 codeape에 의해 입증 된 것과 같은 생성기 함수로 사용하려는 경우 :

def bytes_from_file(filename):
    with open(filename, "rb") as f:
        while True:
            byte = f.read(1)
            if not byte:
                break
            yield(ord(byte))

# example:
for b in bytes_from_file('filename'):
    do_stuff_with(b)

파이썬에서 이진 파일을 읽고 각 바이트를 반복

Python 3.5의 새로운 기능은 pathlib모듈에서 파일을 바이트 단위로 읽는 편리한 방법을 제공하므로 바이트를 반복 할 수 있습니다. 나는 이것이 괜찮은 (빠르고 더러운 경우) 대답이라고 생각합니다.

import pathlib

for byte in pathlib.Path(path).read_bytes():
    print(byte)

이것이 유일한 답변이라는 점에 흥미가 pathlib있습니다.

파이썬 2에서는 Vinay Sajip이 제안한 것처럼 아마도 이렇게 할 것입니다.

with open(path, 'b') as file:
    for byte in file.read():
        print(byte)

파일이 메모리에서 반복하기에 너무 큰 경우 iter에는 callable, sentinel서명이 있는 함수 ( Python 2 버전)를 사용하여 관용적으로 청크를 만들 수 있습니다 .

with open(path, 'b') as file:
    callable = lambda: file.read(1024)
    sentinel = bytes() # or b''
    for chunk in iter(callable, sentinel): 
        for byte in chunk:
            print(byte)

(몇 가지 다른 답변이 이것을 언급하지만 합리적인 읽기 크기를 제공하는 사람은 거의 없습니다.)

큰 파일 또는 버퍼 / 대화 형 읽기에 대한 모범 사례

Python 3.5 이상에 대한 표준 라이브러리의 관용적 사용을 포함하여이를 수행하는 함수를 만들어 봅시다.

from pathlib import Path
from functools import partial
from io import DEFAULT_BUFFER_SIZE

def file_byte_iterator(path):
    """given a path, return an iterator over the file
    that lazily loads the file
    """
    path = Path(path)
    with path.open('rb') as file:
        reader = partial(file.read1, DEFAULT_BUFFER_SIZE)
        file_iterator = iter(reader, bytes())
        for chunk in file_iterator:
            for byte in chunk:
                yield byte

우리는 사용 file.read1합니다. file.read요청 된 모든 바이트를 얻을 때까지 또는 EOF. file.read1차단을 피할 수 있으며 이로 인해 더 빨리 돌아올 수 있습니다. 다른 답변들도 이것을 언급하지 않습니다.

모범 사례 사용법 시연 :

메가 바이트 (실제로 mebibyte)의 의사 난수 데이터로 파일을 만들어 봅시다 :

import random
import pathlib
path = 'pseudorandom_bytes'
pathobj = pathlib.Path(path)

pathobj.write_bytes(
  bytes(random.randint(0, 255) for _ in range(2**20)))

이제 그것을 반복하고 메모리에 구체화합시다.

>>> l = list(file_byte_iterator(path))
>>> len(l)
1048576

데이터의 모든 부분 (예 : 마지막 100 바이트와 처음 100 바이트)를 검사 할 수 있습니다.

>>> l[-100:]
[208, 5, 156, 186, 58, 107, 24, 12, 75, 15, 1, 252, 216, 183, 235, 6, 136, 50, 222, 218, 7, 65, 234, 129, 240, 195, 165, 215, 245, 201, 222, 95, 87, 71, 232, 235, 36, 224, 190, 185, 12, 40, 131, 54, 79, 93, 210, 6, 154, 184, 82, 222, 80, 141, 117, 110, 254, 82, 29, 166, 91, 42, 232, 72, 231, 235, 33, 180, 238, 29, 61, 250, 38, 86, 120, 38, 49, 141, 17, 190, 191, 107, 95, 223, 222, 162, 116, 153, 232, 85, 100, 97, 41, 61, 219, 233, 237, 55, 246, 181]
>>> l[:100]
[28, 172, 79, 126, 36, 99, 103, 191, 146, 225, 24, 48, 113, 187, 48, 185, 31, 142, 216, 187, 27, 146, 215, 61, 111, 218, 171, 4, 160, 250, 110, 51, 128, 106, 3, 10, 116, 123, 128, 31, 73, 152, 58, 49, 184, 223, 17, 176, 166, 195, 6, 35, 206, 206, 39, 231, 89, 249, 21, 112, 168, 4, 88, 169, 215, 132, 255, 168, 129, 127, 60, 252, 244, 160, 80, 155, 246, 147, 234, 227, 157, 137, 101, 84, 115, 103, 77, 44, 84, 134, 140, 77, 224, 176, 242, 254, 171, 115, 193, 29]

이진 파일을 한 줄씩 반복하지 마십시오

다음을 수행하지 마십시오-이것은 줄 바꿈 문자가 될 때까지 임의의 크기의 청크를 가져옵니다. 청크가 너무 작거나 너무 클 때 너무 느립니다.

    with open(path, 'rb') as file:
        for chunk in file: # text newline iteration - not for bytes
            for byte in chunk:
                yield byte

위의 내용은 의미 적으로 사람이 읽을 수있는 텍스트 파일 (일반 텍스트, 코드, 마크 업, 마크 다운 등 ... 본질적으로 ascii, utf, latin 등 ... 인코딩 된 것)에만 적합합니다.


파이썬 3, 모든 파일을 한번에 읽으십시오 :

with open("filename", "rb") as binary_file:
    # Read the whole file at once
    data = binary_file.read()
    print(data)

data변수를 사용하여 원하는 것을 반복 할 수 있습니다 .


위의 모든 것을 시도하고 @Aaron Hall의 답변을 사용한 후 Window 10, 8Gb RAM 및 Python 3.5 32 비트를 실행하는 컴퓨터에서 ~ 90 Mb 파일에 대한 메모리 오류가 발생했습니다. 동료가 numpy대신 사용하도록 권장했으며 놀라운 일이 아닙니다.

지금까지 테스트 한 전체 바이너리 파일을 읽는 가장 빠른 방법은 다음과 같습니다.

import numpy as np

file = "binary_file.bin"
data = np.fromfile(file, 'u1')

참고

지금까지 다른 방법보다 훨씬 빠르게 그것이 누군가를 돕기를 바랍니다!


읽을 바이너리 데이터가 많은 경우 struct 모듈 을 고려할 수 있습니다 . "C와 Python 유형 간"변환으로 문서화되었지만 바이트는 바이트이며 C 유형으로 작성된 것인지는 중요하지 않습니다. 예를 들어, 이진 데이터에 2 바이트 정수 2 개와 4 바이트 정수 1 개가 포함 된 경우 다음과 같이 읽을 수 있습니다 ( struct문서 에서 가져온 예 ).

>>> struct.unpack('hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)

파일의 내용을 명시 적으로 반복하는 것보다이 방법이 더 편리하거나 빠르거나 둘 다있을 수 있습니다.


빠른 것을 찾고 있다면 몇 년 동안 사용해온 방법이 있습니다.

from array import array

with open( path, 'rb' ) as file:
    data = array( 'B', file.read() ) # buffer the file

# evaluate it's data
for byte in data:
    v = byte # int value
    c = chr(byte)

int 대신 chars를 반복하려면 data = file.read()py3의 bytes () 객체 인을 사용하면됩니다.

참고 URL : https://stackoverflow.com/questions/1035340/reading-binary-file-and-looping-over-each-byte



반응형