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

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



답변

파이썬 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)

파이썬 3.8

지금부터 : = 연산자 덕분에 위의 코드를 더 짧게 작성할 수 있습니다.

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


답변

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

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 문서를 참조하십시오 .


답변

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

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

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

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

with open("filename", "rb") as f:
    bytes_read = f.read(CHUNKSIZE)
    while bytes_read:
        for b in bytes_read:
            process_byte(b)
        bytes_read = f.read(CHUNKSIZE)

with문장은 Python 2.5 이상에서 사용할 수 있습니다.


답변

버퍼링을 무시하고 한 번에 한 바이트 씩 파일을 읽으려면 두 개의 인수 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슬라이스 표기법을 지원합니다. 예를 들어, 위치에서 시작하여 파일에서 바이트를 mm[i:i+len]반환 len합니다.i . 컨텍스트 관리자 프로토콜은 Python 3.2 이전에는 지원되지 않습니다. mm.close()이 경우 명시 적 으로 호출해야합니다 . 를 사용하여 각 바이트를 반복 mmap하면보다 많은 메모리 를 소비 file.read(1)하지만 mmap훨씬 빠릅니다.


답변

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

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:
            yield from chunk

우리는를 사용 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
            yield from chunk

위의 내용은 'b'플래그 없이 열어야하는 의미 적으로 사람이 읽을 수있는 텍스트 파일 (일반 텍스트, 코드, 마크 업, 마크 다운 등과 같은 본질적으로 ASCII, utf, latin 등 … 인코딩 된 것)에만 적합합니다 .


답변

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)


답변

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

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

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