Trong Python, làm thế nào để tôi đọc trong tệp nhị phân và lặp qua từng byte của tệp đó?
Trong Python, làm thế nào để tôi đọc trong tệp nhị phân và lặp qua từng byte của tệp đó?
Câu trả lời:
Python 2.4 và trước đó
f = open("myfile", "rb")
try:
byte = f.read(1)
while byte != "":
# Do stuff with byte.
byte = f.read(1)
finally:
f.close()
Trăn 2,5-2,7
with open("myfile", "rb") as f:
byte = f.read(1)
while byte != "":
# Do stuff with byte.
byte = f.read(1)
Lưu ý rằng câu lệnh with không có sẵn trong các phiên bản Python dưới 2.5. Để sử dụng nó trong phiên bản 2.5, bạn sẽ cần nhập nó:
from __future__ import with_statement
Trong 2.6 điều này là không cần thiết.
Con trăn 3
Trong Python 3, nó hơi khác một chút. Chúng ta sẽ không còn nhận được các ký tự thô từ luồng trong chế độ byte mà là các đối tượng byte, do đó chúng ta cần thay đổi điều kiện:
with open("myfile", "rb") as f:
byte = f.read(1)
while byte != b"":
# Do stuff with byte.
byte = f.read(1)
Hoặc như benhoyt nói, bỏ qua không bằng và tận dụng thực tế b""
đánh giá là sai. Điều này làm cho mã tương thích giữa 2.6 và 3.x mà không có bất kỳ thay đổi nào. Nó cũng sẽ giúp bạn không thay đổi điều kiện nếu bạn chuyển từ chế độ byte sang văn bản hoặc ngược lại.
with open("myfile", "rb") as f:
byte = f.read(1)
while byte:
# Do stuff with byte.
byte = f.read(1)
trăn 3,8
Từ giờ trở đi, nhờ: = toán tử, đoạn mã trên có thể được viết theo cách ngắn hơn.
with open("myfile", "rb") as f:
while (byte := f.read(1)):
# Do stuff with byte.
Trình tạo này mang lại byte từ một tệp, đọc tệp theo từng đoạn:
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)
Xem tài liệu Python để biết thông tin về các trình vòng lặp và trình tạo .
8192 Byte = 8 kB
(thực ra là vậy KiB
nhưng điều đó không được biết đến nhiều như vậy). Giá trị là "hoàn toàn" ngẫu nhiên nhưng 8 kB dường như là một giá trị phù hợp: không có quá nhiều bộ nhớ bị lãng phí và vẫn không có quá nhiều thao tác đọc như trong câu trả lời được chấp nhận của Skurmedel ...
for b in chunk:
vòng lặp bên trong nhất bằng yield from chunk
. Hình thức này yield
đã được thêm vào Python 3.3 (xem Biểu thức Yield ).
Nếu tệp không quá lớn mà việc giữ nó trong bộ nhớ là một vấn đề:
with open("filename", "rb") as f:
bytes_read = f.read()
for b in bytes_read:
process_byte(b)
trong đó process_byte đại diện cho một số thao tác bạn muốn thực hiện trên byte truyền vào.
Nếu bạn muốn xử lý một đoạn tại một thời điểm:
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)
Câu with
lệnh có sẵn trong Python 2.5 trở lên.
Để đọc một tệp - một byte mỗi lần (bỏ qua bộ đệm) - bạn có thể sử dụng hàm dựng sẵn hai đối sốiter(callable, sentinel)
:
with open(filename, 'rb') as file:
for byte in iter(lambda: file.read(1), b''):
# Do stuff with byte
Nó gọi file.read(1)
cho đến khi nó trả về không có gì b''
(bytestring trống). Bộ nhớ không tăng không giới hạn cho các tệp lớn. Bạn có thể chuyển buffering=0
đến open()
, để vô hiệu hóa bộ đệm - nó đảm bảo rằng chỉ một byte được đọc trên mỗi lần lặp (chậm).
with
-statement đóng tệp tự động - bao gồm cả trường hợp khi mã bên dưới đưa ra một ngoại lệ.
Mặc dù có sự hiện diện của bộ đệm nội bộ theo mặc định, nhưng vẫn không hiệu quả khi xử lý một byte mỗi lần. Ví dụ: đây là blackhole.py
tiện ích ăn mọi thứ được cung cấp:
#!/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)
Thí dụ:
$ dd if=/dev/zero bs=1M count=1000 | python3 blackhole.py
Nó xử lý ~ 1,5 GB / giây khi chunksize == 32768
ở trên máy của tôi và chỉ ~ 7,5 MB / s khi chunksize == 1
. Đó là, chậm hơn 200 lần để đọc một byte mỗi lần. Hãy tính đến nó nếu bạn có thể viết lại quá trình xử lý của mình để sử dụng nhiều hơn một byte mỗi lần và nếu bạn cần hiệu suất.
mmap
cho phép bạn xử lý bytearray
đồng thời một tệp và một đối tượng tệp. Nó có thể phục vụ như là một thay thế để tải toàn bộ tệp trong bộ nhớ nếu bạn cần truy cập cả hai giao diện. Cụ thể, bạn có thể lặp lại một byte mỗi lần trên một tệp ánh xạ bộ nhớ chỉ bằng cách sử dụng một đơn giảnfor
:
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
hỗ trợ ký hiệu lát. Ví dụ, mm[i:i+len]
trả về len
byte từ tệp bắt đầu tại vị trí i
. Giao thức quản lý bối cảnh không được hỗ trợ trước Python 3.2; bạn cần gọi mm.close()
một cách rõ ràng trong trường hợp này. Lặp lại trên mỗi byte sử dụng mmap
sẽ tiêu tốn nhiều bộ nhớ hơn file.read(1)
, nhưng mmap
là một thứ tự cường độ nhanh hơn.
numpy
mảng (byte) ánh xạ bộ nhớ tương đương .
numpy.memmap()
và bạn có thể nhận được một byte dữ liệu tại một thời điểm (ctypes.data). Bạn có thể nghĩ về các mảng numpy chỉ hơn một chút so với các đốm trong bộ nhớ + siêu dữ liệu.
Đọc tệp nhị phân trong Python và lặp qua từng byte
Điểm mới trong Python 3.5 là pathlib
mô-đun, có một phương thức tiện lợi đặc biệt để đọc trong tệp dưới dạng byte, cho phép chúng ta lặp lại qua các byte. Tôi coi đây là một câu trả lời đàng hoàng (nếu nhanh và bẩn):
import pathlib
for byte in pathlib.Path(path).read_bytes():
print(byte)
Thật thú vị khi đây là câu trả lời duy nhất để đề cập đến pathlib
.
Trong Python 2, có lẽ bạn sẽ làm điều này (như Vinay Sajip cũng gợi ý):
with open(path, 'b') as file:
for byte in file.read():
print(byte)
Trong trường hợp tệp có thể quá lớn để lặp lại trong bộ nhớ, bạn sẽ chunk nó, một cách tự nhiên, sử dụng iter
hàm có callable, sentinel
chữ ký - phiên bản 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)
(Một số câu trả lời khác đề cập đến điều này, nhưng một số ít cung cấp kích thước đọc hợp lý.)
Hãy tạo một hàm để làm điều này, bao gồm cả việc sử dụng thành ngữ của thư viện chuẩn cho 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
Lưu ý rằng chúng tôi sử dụng file.read1
. file.read
chặn cho đến khi nó nhận được tất cả các byte được yêu cầu của nó hoặc EOF
.file.read1
cho phép chúng tôi tránh bị chặn và nó có thể quay lại nhanh hơn vì điều này. Không có câu trả lời khác đề cập đến điều này là tốt.
Chúng ta hãy tạo một tệp với một megabyte (thực sự là mebibyte) của dữ liệu giả ngẫu nhiên:
import random
import pathlib
path = 'pseudorandom_bytes'
pathobj = pathlib.Path(path)
pathobj.write_bytes(
bytes(random.randint(0, 255) for _ in range(2**20)))
Bây giờ chúng ta hãy lặp lại nó và cụ thể hóa nó trong bộ nhớ:
>>> l = list(file_byte_iterator(path))
>>> len(l)
1048576
Chúng tôi có thể kiểm tra bất kỳ phần nào của dữ liệu, ví dụ: 100 byte cuối cùng và 100 byte đầu tiên:
>>> 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]
Đừng làm như sau - điều này kéo một đoạn có kích thước tùy ý cho đến khi nó chuyển sang ký tự dòng mới - quá chậm khi các khối quá nhỏ và cũng có thể quá lớn:
with open(path, 'rb') as file:
for chunk in file: # text newline iteration - not for bytes
yield from chunk
Ở trên chỉ tốt cho các tệp văn bản có thể đọc được về mặt ngữ nghĩa của con người (như văn bản đơn giản, mã, đánh dấu, đánh dấu, v.v ... về cơ bản là bất cứ thứ gì được mã hóa, utf, latin, v.v.) mà bạn nên mở mà không cần 'b'
cờ.
path = Path(path), with path.open('rb') as file:
thay vì sử dụng chức năng mở tích hợp thay thế không? Cả hai cùng làm điều đúng?
Path
đối tượng vì đó là một cách mới rất thuận tiện để xử lý các đường dẫn. Thay vì chuyển một chuỗi vào các hàm "đúng" được chọn cẩn thận, chúng ta chỉ cần gọi các phương thức trên đối tượng đường dẫn, về cơ bản chứa hầu hết các chức năng quan trọng mà bạn muốn với chuỗi đường dẫn. Với các IDE có thể kiểm tra, chúng ta cũng có thể dễ dàng tự động hoàn thành hơn. Chúng ta có thể thực hiện tương tự với open
nội trang, nhưng có rất nhiều mặt tích cực khi viết chương trình cho lập trình viên sử dụng Path
đối tượng thay thế.
file_byte_iterator
nhanh hơn nhiều so với tất cả các phương pháp tôi đã thử trên trang này. Thanh danh cho bạn!
Để tổng hợp tất cả các điểm tuyệt vời của chrispy, Skurmedel, Ben Hoyt và Peter Hansen, đây sẽ là giải pháp tối ưu để xử lý tệp nhị phân một byte mỗi lần:
with open("myfile", "rb") as f:
while True:
byte = f.read(1)
if not byte:
break
do_stuff_with(ord(byte))
Đối với phiên bản python 2.6 trở lên, bởi vì:
Hoặc sử dụng giải pháp JF Sebastians để cải thiện tốc độ
from functools import partial
with open(filename, 'rb') as file:
for byte in iter(partial(file.read, 1), b''):
# Do stuff with byte
Hoặc nếu bạn muốn nó là một hàm tạo như được trình bày bởi 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)
Sau khi thử tất cả các câu hỏi trên và sử dụng câu trả lời từ @Aaron Hall, tôi đã gặp lỗi bộ nhớ cho tệp ~ 90 Mb trên máy tính chạy Window 10, RAM 8 Gb và Python 3.5 32 bit. Tôi đã được một đồng nghiệp giới thiệu để sử dụng numpy
thay thế và nó hoạt động kỳ diệu.
Cho đến nay, nhanh nhất để đọc toàn bộ tệp nhị phân (mà tôi đã kiểm tra) là:
import numpy as np
file = "binary_file.bin"
data = np.fromfile(file, 'u1')
Đa số nhanh hơn bất kỳ phương pháp nào cho đến nay. Hy vọng nó sẽ giúp được ai đó!
numpy
, thì có thể đáng giá.
Nếu bạn có nhiều dữ liệu nhị phân để đọc, bạn có thể muốn xem xét mô-đun struct . Nó được ghi nhận là chuyển đổi "giữa các loại C và Python", nhưng tất nhiên, byte là byte và việc chúng được tạo như loại C không thành vấn đề. Ví dụ: nếu dữ liệu nhị phân của bạn chứa hai số nguyên 2 byte và một số nguyên 4 byte, bạn có thể đọc chúng như sau (ví dụ được lấy từ struct
tài liệu):
>>> struct.unpack('hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)
Bạn có thể thấy điều này thuận tiện hơn, nhanh hơn hoặc cả hai, hơn là lặp lại rõ ràng nội dung của một tệp.
Bài viết này không phải là một câu trả lời trực tiếp cho câu hỏi. Thay vào đó, điểm chuẩn mở rộng dựa trên dữ liệu có thể được sử dụng để so sánh nhiều câu trả lời (và các biến thể sử dụng các tính năng mới được thêm vào sau này, các phiên bản Python hiện đại hơn) đã được đăng vào câu hỏi này - và do đó nên có ích trong việc xác định cái nào có hiệu suất tốt nhất.
Trong một vài trường hợp, tôi đã sửa đổi mã trong câu trả lời được tham chiếu để làm cho nó tương thích với khung chuẩn.
Đầu tiên, đây là kết quả cho những gì hiện tại là phiên bản mới nhất của Python 2 & 3:
Fastest to slowest execution speeds with 32-bit Python 2.7.16
numpy version 1.16.5
Test file size: 1,024 KiB
100 executions, best of 3 repetitions
1 Tcll (array.array) : 3.8943 secs, rel speed 1.00x, 0.00% slower (262.95 KiB/sec)
2 Vinay Sajip (read all into memory) : 4.1164 secs, rel speed 1.06x, 5.71% slower (248.76 KiB/sec)
3 codeape + iter + partial : 4.1616 secs, rel speed 1.07x, 6.87% slower (246.06 KiB/sec)
4 codeape : 4.1889 secs, rel speed 1.08x, 7.57% slower (244.46 KiB/sec)
5 Vinay Sajip (chunked) : 4.1977 secs, rel speed 1.08x, 7.79% slower (243.94 KiB/sec)
6 Aaron Hall (Py 2 version) : 4.2417 secs, rel speed 1.09x, 8.92% slower (241.41 KiB/sec)
7 gerrit (struct) : 4.2561 secs, rel speed 1.09x, 9.29% slower (240.59 KiB/sec)
8 Rick M. (numpy) : 8.1398 secs, rel speed 2.09x, 109.02% slower (125.80 KiB/sec)
9 Skurmedel : 31.3264 secs, rel speed 8.04x, 704.42% slower ( 32.69 KiB/sec)
Benchmark runtime (min:sec) - 03:26
Fastest to slowest execution speeds with 32-bit Python 3.8.0
numpy version 1.17.4
Test file size: 1,024 KiB
100 executions, best of 3 repetitions
1 Vinay Sajip + "yield from" + "walrus operator" : 3.5235 secs, rel speed 1.00x, 0.00% slower (290.62 KiB/sec)
2 Aaron Hall + "yield from" : 3.5284 secs, rel speed 1.00x, 0.14% slower (290.22 KiB/sec)
3 codeape + iter + partial + "yield from" : 3.5303 secs, rel speed 1.00x, 0.19% slower (290.06 KiB/sec)
4 Vinay Sajip + "yield from" : 3.5312 secs, rel speed 1.00x, 0.22% slower (289.99 KiB/sec)
5 codeape + "yield from" + "walrus operator" : 3.5370 secs, rel speed 1.00x, 0.38% slower (289.51 KiB/sec)
6 codeape + "yield from" : 3.5390 secs, rel speed 1.00x, 0.44% slower (289.35 KiB/sec)
7 jfs (mmap) : 4.0612 secs, rel speed 1.15x, 15.26% slower (252.14 KiB/sec)
8 Vinay Sajip (read all into memory) : 4.5948 secs, rel speed 1.30x, 30.40% slower (222.86 KiB/sec)
9 codeape + iter + partial : 4.5994 secs, rel speed 1.31x, 30.54% slower (222.64 KiB/sec)
10 codeape : 4.5995 secs, rel speed 1.31x, 30.54% slower (222.63 KiB/sec)
11 Vinay Sajip (chunked) : 4.6110 secs, rel speed 1.31x, 30.87% slower (222.08 KiB/sec)
12 Aaron Hall (Py 2 version) : 4.6292 secs, rel speed 1.31x, 31.38% slower (221.20 KiB/sec)
13 Tcll (array.array) : 4.8627 secs, rel speed 1.38x, 38.01% slower (210.58 KiB/sec)
14 gerrit (struct) : 5.0816 secs, rel speed 1.44x, 44.22% slower (201.51 KiB/sec)
15 Rick M. (numpy) + "yield from" : 11.8084 secs, rel speed 3.35x, 235.13% slower ( 86.72 KiB/sec)
16 Skurmedel : 11.8806 secs, rel speed 3.37x, 237.18% slower ( 86.19 KiB/sec)
17 Rick M. (numpy) : 13.3860 secs, rel speed 3.80x, 279.91% slower ( 76.50 KiB/sec)
Benchmark runtime (min:sec) - 04:47
Tôi cũng đã chạy nó với tệp thử nghiệm 10 MiB lớn hơn nhiều (mất gần một giờ để chạy) và nhận được kết quả hiệu suất tương đương với các tệp được hiển thị ở trên.
Đây là mã được sử dụng để làm điểm chuẩn:
from __future__ import print_function
import array
import atexit
from collections import deque, namedtuple
import io
from mmap import ACCESS_READ, mmap
import numpy as np
from operator import attrgetter
import os
import random
import struct
import sys
import tempfile
from textwrap import dedent
import time
import timeit
import traceback
try:
xrange
except NameError: # Python 3
xrange = range
class KiB(int):
""" KibiBytes - multiples of the byte units for quantities of information. """
def __new__(self, value=0):
return 1024*value
BIG_TEST_FILE = 1 # MiBs or 0 for a small file.
SML_TEST_FILE = KiB(64)
EXECUTIONS = 100 # Number of times each "algorithm" is executed per timing run.
TIMINGS = 3 # Number of timing runs.
CHUNK_SIZE = KiB(8)
if BIG_TEST_FILE:
FILE_SIZE = KiB(1024) * BIG_TEST_FILE
else:
FILE_SIZE = SML_TEST_FILE # For quicker testing.
# Common setup for all algorithms -- prefixed to each algorithm's setup.
COMMON_SETUP = dedent("""
# Make accessible in algorithms.
from __main__ import array, deque, get_buffer_size, mmap, np, struct
from __main__ import ACCESS_READ, CHUNK_SIZE, FILE_SIZE, TEMP_FILENAME
from functools import partial
try:
xrange
except NameError: # Python 3
xrange = range
""")
def get_buffer_size(path):
""" Determine optimal buffer size for reading files. """
st = os.stat(path)
try:
bufsize = st.st_blksize # Available on some Unix systems (like Linux)
except AttributeError:
bufsize = io.DEFAULT_BUFFER_SIZE
return bufsize
# Utility primarily for use when embedding additional algorithms into benchmark.
VERIFY_NUM_READ = """
# Verify generator reads correct number of bytes (assumes values are correct).
bytes_read = sum(1 for _ in file_byte_iterator(TEMP_FILENAME))
assert bytes_read == FILE_SIZE, \
'Wrong number of bytes generated: got {:,} instead of {:,}'.format(
bytes_read, FILE_SIZE)
"""
TIMING = namedtuple('TIMING', 'label, exec_time')
class Algorithm(namedtuple('CodeFragments', 'setup, test')):
# Default timeit "stmt" code fragment.
_TEST = """
#for b in file_byte_iterator(TEMP_FILENAME): # Loop over every byte.
# pass # Do stuff with byte...
deque(file_byte_iterator(TEMP_FILENAME), maxlen=0) # Data sink.
"""
# Must overload __new__ because (named)tuples are immutable.
def __new__(cls, setup, test=None):
""" Dedent (unindent) code fragment string arguments.
Args:
`setup` -- Code fragment that defines things used by `test` code.
In this case it should define a generator function named
`file_byte_iterator()` that will be passed that name of a test file
of binary data. This code is not timed.
`test` -- Code fragment that uses things defined in `setup` code.
Defaults to _TEST. This is the code that's timed.
"""
test = cls._TEST if test is None else test # Use default unless one is provided.
# Uncomment to replace all performance tests with one that verifies the correct
# number of bytes values are being generated by the file_byte_iterator function.
#test = VERIFY_NUM_READ
return tuple.__new__(cls, (dedent(setup), dedent(test)))
algorithms = {
'Aaron Hall (Py 2 version)': Algorithm("""
def file_byte_iterator(path):
with open(path, "rb") as file:
callable = partial(file.read, 1024)
sentinel = bytes() # or b''
for chunk in iter(callable, sentinel):
for byte in chunk:
yield byte
"""),
"codeape": Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
while True:
chunk = f.read(chunksize)
if chunk:
for b in chunk:
yield b
else:
break
"""),
"codeape + iter + partial": Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
for chunk in iter(partial(f.read, chunksize), b''):
for b in chunk:
yield b
"""),
"gerrit (struct)": Algorithm("""
def file_byte_iterator(filename):
with open(filename, "rb") as f:
fmt = '{}B'.format(FILE_SIZE) # Reads entire file at once.
for b in struct.unpack(fmt, f.read()):
yield b
"""),
'Rick M. (numpy)': Algorithm("""
def file_byte_iterator(filename):
for byte in np.fromfile(filename, 'u1'):
yield byte
"""),
"Skurmedel": Algorithm("""
def file_byte_iterator(filename):
with open(filename, "rb") as f:
byte = f.read(1)
while byte:
yield byte
byte = f.read(1)
"""),
"Tcll (array.array)": Algorithm("""
def file_byte_iterator(filename):
with open(filename, "rb") as f:
arr = array.array('B')
arr.fromfile(f, FILE_SIZE) # Reads entire file at once.
for b in arr:
yield b
"""),
"Vinay Sajip (read all into memory)": Algorithm("""
def file_byte_iterator(filename):
with open(filename, "rb") as f:
bytes_read = f.read() # Reads entire file at once.
for b in bytes_read:
yield b
"""),
"Vinay Sajip (chunked)": Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
chunk = f.read(chunksize)
while chunk:
for b in chunk:
yield b
chunk = f.read(chunksize)
"""),
} # End algorithms
#
# Versions of algorithms that will only work in certain releases (or better) of Python.
#
if sys.version_info >= (3, 3):
algorithms.update({
'codeape + iter + partial + "yield from"': Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
for chunk in iter(partial(f.read, chunksize), b''):
yield from chunk
"""),
'codeape + "yield from"': Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
while True:
chunk = f.read(chunksize)
if chunk:
yield from chunk
else:
break
"""),
"jfs (mmap)": Algorithm("""
def file_byte_iterator(filename):
with open(filename, "rb") as f, \
mmap(f.fileno(), 0, access=ACCESS_READ) as s:
yield from s
"""),
'Rick M. (numpy) + "yield from"': Algorithm("""
def file_byte_iterator(filename):
# data = np.fromfile(filename, 'u1')
yield from np.fromfile(filename, 'u1')
"""),
'Vinay Sajip + "yield from"': Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
chunk = f.read(chunksize)
while chunk:
yield from chunk # Added in Py 3.3
chunk = f.read(chunksize)
"""),
}) # End Python 3.3 update.
if sys.version_info >= (3, 5):
algorithms.update({
'Aaron Hall + "yield from"': Algorithm("""
from pathlib import Path
def file_byte_iterator(path):
''' Given a path, return an iterator over the file
that lazily loads the file.
'''
path = Path(path)
bufsize = get_buffer_size(path)
with path.open('rb') as file:
reader = partial(file.read1, bufsize)
for chunk in iter(reader, bytes()):
yield from chunk
"""),
}) # End Python 3.5 update.
if sys.version_info >= (3, 8, 0):
algorithms.update({
'Vinay Sajip + "yield from" + "walrus operator"': Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
while chunk := f.read(chunksize):
yield from chunk # Added in Py 3.3
"""),
'codeape + "yield from" + "walrus operator"': Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
while chunk := f.read(chunksize):
yield from chunk
"""),
}) # End Python 3.8.0 update.update.
#### Main ####
def main():
global TEMP_FILENAME
def cleanup():
""" Clean up after testing is completed. """
try:
os.remove(TEMP_FILENAME) # Delete the temporary file.
except Exception:
pass
atexit.register(cleanup)
# Create a named temporary binary file of pseudo-random bytes for testing.
fd, TEMP_FILENAME = tempfile.mkstemp('.bin')
with os.fdopen(fd, 'wb') as file:
os.write(fd, bytearray(random.randrange(256) for _ in range(FILE_SIZE)))
# Execute and time each algorithm, gather results.
start_time = time.time() # To determine how long testing itself takes.
timings = []
for label in algorithms:
try:
timing = TIMING(label,
min(timeit.repeat(algorithms[label].test,
setup=COMMON_SETUP + algorithms[label].setup,
repeat=TIMINGS, number=EXECUTIONS)))
except Exception as exc:
print('{} occurred timing the algorithm: "{}"\n {}'.format(
type(exc).__name__, label, exc))
traceback.print_exc(file=sys.stdout) # Redirect to stdout.
sys.exit(1)
timings.append(timing)
# Report results.
print('Fastest to slowest execution speeds with {}-bit Python {}.{}.{}'.format(
64 if sys.maxsize > 2**32 else 32, *sys.version_info[:3]))
print(' numpy version {}'.format(np.version.full_version))
print(' Test file size: {:,} KiB'.format(FILE_SIZE // KiB(1)))
print(' {:,d} executions, best of {:d} repetitions'.format(EXECUTIONS, TIMINGS))
print()
longest = max(len(timing.label) for timing in timings) # Len of longest identifier.
ranked = sorted(timings, key=attrgetter('exec_time')) # Sort so fastest is first.
fastest = ranked[0].exec_time
for rank, timing in enumerate(ranked, 1):
print('{:<2d} {:>{width}} : {:8.4f} secs, rel speed {:6.2f}x, {:6.2f}% slower '
'({:6.2f} KiB/sec)'.format(
rank,
timing.label, timing.exec_time, round(timing.exec_time/fastest, 2),
round((timing.exec_time/fastest - 1) * 100, 2),
(FILE_SIZE/timing.exec_time) / KiB(1), # per sec.
width=longest))
print()
mins, secs = divmod(time.time()-start_time, 60)
print('Benchmark runtime (min:sec) - {:02d}:{:02d}'.format(int(mins),
int(round(secs))))
main()
yield from chunk
thay thế for byte in chunk: yield byte
? Tôi nghĩ tôi nên thắt chặt câu trả lời của mình với điều đó.
yield from
.
enumerate
vì việc lặp lại nên được hiểu là hoàn thành - nếu không, tôi đã kiểm tra lần cuối - liệt kê có một chút chi phí so với việc thực hiện sổ sách kế toán với + = 1, vì vậy bạn có thể thay thế việc ghi sổ trong mã riêng. Hoặc thậm chí vượt qua một deque với maxlen=0
.
enumerate
. Cảm ơn vì bạn đã phản hồi. Sẽ thêm một bản cập nhật cho bài viết của tôi mà không có nó (mặc dù tôi không nghĩ rằng nó thay đổi kết quả nhiều). Cũng sẽ thêm numpy
câu trả lời dựa trên @Rick M.
super().
thay vì tuple.
trong __new__
bạn có thể sử dụng namedtuple
tên thuộc tính thay vì chỉ mục.
nếu bạn đang tìm kiếm thứ gì đó nhanh chóng, thì đây là một phương pháp tôi đã sử dụng trong nhiều năm qua:
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)
nếu bạn muốn lặp lại ký tự thay vì ints, bạn có thể chỉ cần sử dụng data = file.read()
, đó phải là một đối tượng byte () trong py3.