বাইনারি ফাইল পড়া এবং প্রতিটি বাইট উপর লুপিং


378

পাইথনে, আমি কীভাবে বাইনারি ফাইল পড়ব এবং সেই ফাইলের প্রতিটি বাইটের উপর লুপ করব?

উত্তর:


387

পাইথন ২.৪ এবং এর আগে

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)

নোট করুন যে বিবৃতি সহ 2.5 এর নীচে পাইথনের সংস্করণগুলিতে উপলব্ধ নেই। এটি 2.5 ভিতে ব্যবহার করতে আপনাকে এটি আমদানি করতে হবে:

from __future__ import with_statement

2.6 এ এটি প্রয়োজন হয় না।

পাইথন ঘ

পাইথন 3 এ এটি কিছুটা আলাদা। আমরা আর বাইট মোডে বাইট অবজেক্টে স্ট্রিম থেকে কাঁচা অক্ষর পাবো না, সুতরাং আমাদের শর্তটি পরিবর্তন করতে হবে:

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

বা বেনহয়েট যেমন বলেছে, সমান নয় এবং এটিকে যে 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.

40
বাইট-ওয়াইস ফাইল পড়া একটি পারফরম্যান্স দুঃস্বপ্ন। পাইথনে এটি সর্বোত্তম সমাধান হতে পারে না। এই কোডটি যত্ন সহ ব্যবহার করা উচিত।
usr ডিরেক্টরির

7
@ অ্যাসার: আচ্ছা ফাইলের বস্তুগুলি অভ্যন্তরীণভাবে বাফার হয় এবং এরপরেও এটি অনুরোধ করা হয়েছিল। প্রতিটি স্ক্রিপ্টের অনুকূল কর্মক্ষমতা প্রয়োজন হয় না।
স্কুরমেডেল

4
@ মেঝাকা: সুতরাং আপনি এটি পড়ুন (1) থেকে পড়তে (বুফসাইজ) পরিবর্তন করুন এবং কিছুক্ষণের মধ্যে আপনি একটি ইন-ইন করেন ... উদাহরণটি এখনও দাঁড়িয়ে আছে।
Skurmedel

3
@ আরআর: আমার চেষ্টা করা কোডটির জন্য পারফরম্যান্সের পার্থক্য 200 গুণ হতে পারে ।
jfs

2
@ আরআর - আপনি কতগুলি বাইট প্রক্রিয়া করতে চান তার উপর নির্ভর করে। যদি তারা যথেষ্ট পরিমাণে হয় তবে "খারাপভাবে" পারফর্ম করছে তবে সহজেই বোধগম্য কোডটি অনেক বেশি পছন্দ করা যেতে পারে। কোডটি রক্ষণাবেক্ষণের সময় "রিডার সিপিইউ চক্র" সংরক্ষণের জন্য সিপিইউ চক্রের অপচয়কে ক্ষতিপূরণ দেওয়া হয়।
IllvilJa

172

এই জেনারেটর একটি ফাইল থেকে বাইটস উত্পাদন করে, খণ্ডগুলিতে ফাইলটি পড়ে:

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)

তথ্যের জন্য পাইথন ডকুমেন্টেশন দেখুন iterators এবং জেনারেটর


3
@ কোডেপ ঠিক আমি যা খুঁজছি তবে, আপনি কীভাবে চুনসাইজ নির্ধারণ করবেন? এটি একটি নির্বিচার মান হতে পারে?
সোদেব

3
@swdev: উদাহরণস্বরূপ 8192 একটি chunksize ব্যবহার বাইট । ফাইল.প্রেড () - এর জন্য প্যারামিটারটি সহজেই আকার নির্দিষ্ট করে, অর্থাত বাইটসের সংখ্যাটি পড়তে হবে। কোড্যাপ বেছে নিয়েছে 8192 Byte = 8 kB(আসলে এটি KiBকিন্তু এটি সাধারণভাবে জানা যায় না)। মানটি "সম্পূর্ণরূপে" এলোমেলো তবে 8 কেবি উপযুক্ত মান বলে মনে হচ্ছে: খুব বেশি মেমরি নষ্ট হয় না এবং এখনও স্কুরমেডেলের গৃহীত উত্তরের মতো "খুব বেশি" পঠিত অপারেশন নেই ...
মোজবোজ

3
ফাইল সিস্টেমটি ইতিমধ্যে ডেটার অংশগুলিকে বাফার করে, সুতরাং এই কোডটি নিরর্থক। একবারে বাইট পড়া ভাল।
সম্পূর্ণ

17
গৃহীত উত্তরের চেয়ে ইতিমধ্যে দ্রুততর অবস্থায়, পুরো অভ্যন্তরীণ- for b in chunk:লুপটি প্রতিস্থাপনের মাধ্যমে এটি আরও 20-25% দ্বারা বৃদ্ধি পেতে পারে yield from chunk। এই ফর্মটি yieldপাইথন ৩.৩-এ যুক্ত করা হয়েছে ( ফলনের এক্সপ্রেশন দেখুন )।
মার্টিনিউ

3
হুম মনে হয় অসম্ভব, লিঙ্ক?
কোডেপ

54

ফাইলটি যদি খুব বড় না হয় তবে এটি মেমোরিতে রাখা একটি সমস্যা:

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

যেখানে প্রসেস_বাইট কিছু অপারেশনকে প্রতিনিধিত্ব করে যা আপনি পাস-ইন বাইটে সম্পাদন করতে চান।

আপনি যদি একবারে কিছু অংশ প্রক্রিয়া করতে চান:

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বিবৃতি পাইথন 2.5 ও বৃহত্তর পাওয়া যায়।


1
আপনি সবেমাত্র পোস্ট করা মানদণ্ডে আপনার আগ্রহী হতে পারে ।
মার্টিনো

37

একটি ফাইল পড়তে - একবারে এক বাইট (বাফারিং উপেক্ষা করে) - আপনি দ্বি-যুক্তি 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=0 করতে open(), পাস করতে পারেন - এটি গ্যারান্টি দেয় যে প্রতি পুনরাবৃত্তি (ধীর) প্রতি এক বাইট পড়বে।

with-সেটেটমেন্ট স্বয়ংক্রিয়ভাবে ফাইলটি বন্ধ করে দেয় - নীচের কোডটি ব্যতিক্রম উত্থাপন করে এমন কেস সহ।

ডিফল্টরূপে অভ্যন্তরীণ বাফারিংয়ের উপস্থিতি সত্ত্বেও, একবারে এক বাইট প্রক্রিয়াজাতকরণ এটি এখনও অদক্ষ। উদাহরণস্বরূপ, এখানে 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 গিগাবাইট / সেকেন্ড হয় এবং যখন chunksize == 32768কেবল ~ 7.5 এমবি / সেগুলি হয় chunksize == 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। পাইথন ৩.২ এর আগে কনটেক্সট ম্যানেজার প্রোটোকল সমর্থিত নয়; mm.close()এই ক্ষেত্রে আপনার স্পষ্টভাবে কল করতে হবে । প্রতিটি বাইট ব্যবহার mmapকরে আইট্রেট করা তুলনায় বেশি মেমরি গ্রহণ করে file.read(1)তবে mmapএটি দ্রুততার একটি ক্রম।


আমি শেষ উদাহরণটি খুব আকর্ষণীয় পেয়েছি। খুব খারাপ কোনও সমান numpyমেমরি-ম্যাপযুক্ত (বাইট) অ্যারে নেই।
মার্টিনো 12

1
@ মার্টিনিউ আছে numpy.memmap()এবং আপনি একবারে ডেটা বাইট পেতে পারেন (সিটিপিস.ডেটা)। আপনি স্মৃতিশক্তি + মেটাডেটাতে ব্লাবিগুলির চেয়ে অল্প অল্প অ্যারেগুলিকে একটু বেশিই ভাবতে পারেন।
jfs

jfs: ধন্যবাদ, দুর্দান্ত খবর! এর অস্তিত্ব আছে এমন কিছুই জানতেন না। দুর্দান্ত উত্তর, বিটিডাব্লু।
মার্টিনো

25

পাইথনে বাইনারি ফাইল পড়া এবং প্রতিটি বাইটের উপর লুপ করা

পাইথন ৩.৫-এ নতুন হল pathlibমডিউলটি, যা একটি ফাইলে বিশেষত বাইট হিসাবে পড়ার জন্য একটি সুবিধা পদ্ধতি রয়েছে, যা আমাদের বাইটগুলি দিয়ে পুনরাবৃত্তি করতে দেয়। আমি এটিকে একটি শালীন (দ্রুত এবং নোংরা) উত্তর হিসাবে বিবেচনা করছি:

import pathlib

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

আকর্ষণীয় যে এটি উল্লেখ করার একমাত্র উত্তর pathlib

পাইথন 2-এ, আপনি সম্ভবত এটি করতে পারেন (বিনয় সজিপও পরামর্শ দিয়েছেন):

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

মেমোরির মাধ্যমে ফাইলটি পুনরাবৃত্তি করতে খুব বড় আকারের হতে পারে, আপনি স্বতন্ত্রভাবে iterফাংশনটি callable, sentinelস্বাক্ষর সহ ব্যবহার করে অজানাভাবে এটি পিণ্ড করতে পারবেন - পাইথন 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)

(অন্যান্য বেশ কয়েকটি উত্তরে এটি উল্লেখ করা হয়েছে, তবে কয়েকটি কম বুদ্ধিমানের পড়ার আকার দেয়))

বড় ফাইল বা বাফার / ইন্টারেক্টিভ পড়ার জন্য সেরা অনুশীলন

আসুন এটি করার জন্য একটি ফাংশন তৈরি করি, পাইথন 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.read1file.readএটির জন্য অনুরোধ করা সমস্ত বাইট না পাওয়া পর্যন্ত বা অবরুদ্ধ করে EOFfile.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'পতাকা ছাড়াই খোলা উচিত ।


2
এটি অনেক ভাল ... এটি করার জন্য আপনাকে ধন্যবাদ। আমি জানি যে দুই বছরের পুরানো উত্তরে ফিরে যাওয়া সবসময় মজাদার নয়, তবে আপনি এটি করেছেন বলে আমি প্রশংসা করি। আমি বিশেষত "লাইনের মাধ্যমে পুনরাবৃত্তি করবেন না" উপ-শিরোনামটি পছন্দ করি :-)
ফ্লোরিস

1
হাই অ্যারন, এর path = Path(path), with path.open('rb') as file:পরিবর্তে বিল্ট-ইন ওপেন ফাংশনটি ব্যবহার না করে আপনি ব্যবহার করার জন্য কেন কোনও কারণ আছে ? তারা দুজনই কি একই কাজটি সঠিক করে?
জোশুয়া যোনাথন

1
@ জোশুয়া যোনাথন আমি এই Pathবস্তুটি ব্যবহার করি কারণ এটি পথগুলি পরিচালনা করার জন্য খুব সুবিধাজনক নতুন উপায়। সাবধানতার সাথে নির্বাচিত "ডান" ফাংশনগুলিতে স্ট্রিংয়ের পাশ দিয়ে যাওয়ার পরিবর্তে আমরা সহজভাবে পথ অবজেক্টের পদ্ধতিগুলিকে কল করতে পারি, যা মূলত একটি স্ট্রিং স্ট্রিং যা আপনি চান তার মধ্যে বেশিরভাগ গুরুত্বপূর্ণ কার্যকারিতা রয়েছে। যে আইডিইগুলি পরিদর্শন করতে পারে তার সাথে আমরা আরও সহজে স্বতঃপূরণ পেতে পারি। আমরা openবিল্টিন দিয়ে এটি সম্পন্ন করতে পারতাম , তবে প্রোগ্রামারটির Pathপরিবর্তে অবজেক্টটি ব্যবহার করার জন্য প্রোগ্রামটি লেখার সময় প্রচুর উত্সাহ রয়েছে ।
অ্যারন হল

1
আপনি ফাংশনটি ব্যবহার করে উল্লিখিত সর্বশেষ পদ্ধতিটি file_byte_iteratorআমি এই পৃষ্ঠায় চেষ্টা করেছি এমন সমস্ত পদ্ধতির চেয়ে অনেক দ্রুত। আপনাকে কুডোস!
রিক এম

@ রিকএম: আমি সবেমাত্র পোস্ট করা মানদণ্ডে আপনার আগ্রহী হতে পারে ।
মার্টিনো

19

খ্রিস্টির সমস্ত উজ্জ্বল পয়েন্টগুলি সংক্ষিপ্ত করতে, স্কুরমিডেল, বেন হোয়েট এবং পিটার হ্যানসেন, এটি একবারে বাইনারি ফাইল এক বাইট প্রসেস করার সর্বোত্তম সমাধান হতে পারে:

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

পাইথন সংস্করণগুলির জন্য ২.6 বা তার বেশি সংস্করণ, কারণ:

  • অজগরটি অভ্যন্তরীণভাবে বাফার করে - খণ্ডগুলি পড়ার দরকার নেই
  • DRY নীতি - পঠন লাইনের পুনরাবৃত্তি করবেন না
  • বিবৃতি সহ একটি পরিষ্কার ফাইল বন্ধ নিশ্চিত করে
  • 'বাইট' মিথ্যাতে মূল্যায়ন করে যখন কোনও বাইট না থাকে (যখন বাইট শূন্য হয় না)

বা উন্নত গতির জন্য জেএফ সেবাস্তিয়ান সমাধান ব্যবহার করুন

from functools import partial

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

অথবা আপনি যদি এটিকে কোনও জেনারেটর ফাংশন হিসাবে চান যেমন কোডপে দ্বারা প্রদর্শিত:

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)

2
লিঙ্কযুক্ত উত্তর যেমনটি বলেছে, পাঠগুলি বাফার করা হলেও পাইথনে একবারে এক বাইট পড়া / প্রক্রিয়া করা ধীর গতিতে থাকে। লিঙ্কযুক্ত উত্তরে উদাহরণ হিসাবে যেমন একসাথে বেশ কয়েকটি বাইট প্রক্রিয়া করা যেতে পারে তবে পারফরম্যান্স মারাত্মকভাবে উন্নতি করা যেতে পারে: 1.5 জিবি / সে বনাম 7.5 এমবি / গুলি।
jfs

6

পাইথন 3, সমস্ত ফাইল একবারে পড়ুন:

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

আপনি dataভেরিয়েবল ব্যবহার করে যা চান তা পুনরাবৃত্তি করতে পারেন ।


6

উপরের সমস্তটি চেষ্টা করে এবং @ অ্যারন হল থেকে উত্তরটি ব্যবহার করার পরে, আমি উইন্ডো 10, 8 জিবি র‌্যাম এবং পাইথন 3.5 3.5-বিট চলমান কম্পিউটারে একটি 90 ডলার এমবি ফাইলের জন্য মেমরির ত্রুটি পেয়েছি। numpyপরিবর্তে আমাকে ব্যবহার করার জন্য কোনও সহকর্মী দ্বারা প্রস্তাবিত হয়েছিল এবং এটি আশ্চর্যের সাথে কাজ করে।

এতদূর, একটি সম্পূর্ণ বাইনারি ফাইল (যা আমি পরীক্ষা করেছি) পড়ার জন্য দ্রুততমটি হ'ল:

import numpy as np

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

উল্লেখ

এখন পর্যন্ত অন্য যে কোনও পদ্ধতির চেয়ে বহুগুণ দ্রুত। আশা করি এটি কাউকে সাহায্য করবে!


3
দুর্দান্ত, তবে বিভিন্ন ডাটা টাইপযুক্ত বাইনারি ফাইলে ব্যবহার করা যাবে না।
নির্মল

@ নির্মল: প্রশ্নটি পৌঁছনো বাইট ছাড়িয়ে যাওয়ার বিষয়ে, সুতরাং বিভিন্ন ডেটা ধরণের সম্পর্কে আপনার মন্তব্যটির কোনও প্রভাব রয়েছে কিনা তা পরিষ্কার নয়।
মার্টিনো

1
রিক: আপনার কোড অন্যদের মতো ঠিক তেমন কাজ করছে না - যথা প্রতিটি বাইটের উপরে লুপিং। যদি এটিতে এটি যুক্ত হয় তবে কমপক্ষে আমার বেঞ্চমার্কের ফলাফল অনুসারে এটি অন্যদের সংখ্যাগরিষ্ঠের চেয়ে দ্রুততর কিছু নয় । আসলে এটি ধীর পদ্ধতির একটি বলে মনে হচ্ছে। যদি প্রতিটি বাইটে প্রক্রিয়াজাতকরণ (যা কিছু হতে পারে) এর মাধ্যমে সম্পন্ন numpyহতে পারে তবে তা সার্থক হতে পারে।
মার্টিনিউ

@ মার্টিনো আপনার মন্তব্যের জন্য ধন্যবাদ, হ্যাঁ আমি বুঝতে পেরেছি যে প্রশ্নটি প্রতিটি বাইটের উপর দিয়ে লুপিং করা এবং কেবল একসাথে সমস্ত কিছু লোড করার বিষয়ে নয়, তবে এই প্রশ্নের আরও উত্তর রয়েছে যা সমস্ত বিষয়বস্তু পড়ার দিকেও ইঙ্গিত করে এবং তাই আমার উত্তর
রিক এম

4

আপনার যদি পড়ার মতো প্রচুর বাইনারি ডেটা থাকে তবে আপনি স্ট্রাক্ট মডিউলটি বিবেচনা করতে পারেন । এটি "সি এবং পাইথনের ধরণের মধ্যে রূপান্তরকারী হিসাবে নথিভুক্ত করা হয়, তবে অবশ্যই বাইটগুলি বাইট হয় এবং সেগুলি সি প্রকার হিসাবে তৈরি হয়েছিল কিনা তাতে কিছু যায় আসে না। উদাহরণস্বরূপ, যদি আপনার বাইনারি ডেটাতে দুটি 2-বাইট পূর্ণসংখ্যা এবং একটি 4-বাইট পূর্ণসংখ্যা থাকে তবে আপনি সেগুলি নীচে পড়তে পারেন (উদাহরণস্বরূপ structডকুমেন্টেশন থেকে নেওয়া ):

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

কোনও ফাইলের সামগ্রীতে স্পষ্টভাবে লুপিংয়ের চেয়ে আপনি এটি আরও সুবিধাজনক, দ্রুত এবং উভয়ই পেতে পারেন।


4

এই পোস্টটি নিজেই প্রশ্নের সরাসরি উত্তর নয়। এর পরিবর্তে এটি একটি ডেটা-চালিত এক্সটেনসিবল বেঞ্চমার্ক যা এই প্রশ্নটিতে পোস্ট করা উত্তরগুলির (এবং পরবর্তীকালে আরও আধুনিক, পাইথনের সংস্করণগুলিতে যুক্ত হওয়া নতুন বৈশিষ্ট্যগুলি ব্যবহারের বিভিন্নতা) তুলনা করতে ব্যবহার করা যেতে পারে - এবং তাই উচিত কোনটির সেরা পারফরম্যান্স রয়েছে তা নির্ধারণে সহায়ক হোন।

কয়েকটি ক্ষেত্রে আমি কোডটিকে বেঞ্চমার্ক কাঠামোর সাথে সামঞ্জস্যপূর্ণ করার জন্য রেফারেন্সড উত্তরে সংশোধন করেছি।

প্রথমত, পাইথন 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

আমি এটিকে আরও বড় 10 টি এমআইবি পরীক্ষার ফাইল দিয়ে চালিয়েছি (যা চালাতে প্রায় এক ঘন্টা সময় নিয়েছিল) এবং পারফরম্যান্সের ফলাফল পেয়েছি যা উপরের চিত্রের সাথে তুলনীয়।

বেঞ্চমার্কিং করতে ব্যবহৃত কোডটি এখানে:

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বদলে করব for byte in chunk: yield byte? আমি ভাবছি এটি দিয়ে আমার উত্তরটি আরও শক্ত করা উচিত।
হারুন হলের

@ অ্যারন: পাইথন 3 ফলাফলগুলিতে আপনার উত্তর দুটি সংস্করণ রয়েছে এবং এর মধ্যে একটি ব্যবহার করে yield from
মার্টিনিউ

ঠিক আছে, আমি আমার উত্তর আপডেট করেছি। এছাড়াও আমি আপনাকে প্রস্তাবটি হ্রাস করলাম enumerateযেহেতু পুনরাবৃত্তিটি সম্পূর্ণ বুঝতে হবে - যদি না হয় তবে সর্বশেষে আমি যাচাই করেছিলাম - গণনাটির সূচকটির জন্য বুককিপিংয়ের জন্য + = 1 দিয়ে ব্যয় সহ কিছুটা ওভারহেড থাকে, তাই আপনি বিকল্পভাবে আপনার বুককিপিংটি করতে পারেন নিজস্ব কোড বা এমনকি একটি deque পাস maxlen=0
হারুন হলের

@Aaron: সম্পর্কে সম্মত enumerate। সাহায্য করার জন্য ধন্যবাদ. আমার পোস্টে এমন একটি আপডেট যুক্ত করা হবে যা এতে নেই (যদিও আমি মনে করি না যে এটি ফলাফলগুলি খুব বেশি পরিবর্তন করে)। @ রিক এম এর numpyভিত্তিক উত্তরও যুক্ত করা হবে।
মার্টিনিউ

কিছুটা আরও কোড পর্যালোচনা: পাইথন 2-এর উত্তরটি লেখার কোনও অর্থ হয় না বলে আমি মনে করি না - আমি পাইথন 2 অপসারণ বিবেচনা করব কারণ আমি আশা করি যে আপনি 64 বিট পাইথন 3.7 বা 3.8 ব্যবহার করবেন। আপনি অ্যাক্সিট এবং আংশিক অ্যাপ্লিকেশন দিয়ে শেষে ক্লিনআপটি সেট করতে পারেন। টাইপো: "যাচাইকরণ"। পরীক্ষার স্ট্রিংয়ের সদৃশতায় আমি কোনও বোধগম্য দেখছি না - এগুলি কি একেবারেই আলাদা? আমি আপনি ব্যবহার কল্পনা super().পরিবর্তে tuple.আপনার __new__আপনি ব্যবহার করতে পারে namedtupleইনডেক্স পরিবর্তে অ্যাট্রিবিউট নাম থাকবে না।
হারুন হলের

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)

আপনি যদি ints এর পরিবর্তে চরগুলি পুনরাবৃত্তি করতে চান, আপনি কেবল ব্যবহার করতে পারেন data = file.read(), যা পাই 3 এ বাইট () অবজেক্ট হওয়া উচিত।


1
'অ্যারে' "অ্যারে আমদানি অ্যারে থেকে" আমদানি করা হয়
quanly_mc

@ কোয়ানলি_এমসি হ্যাঁ, এটি ধরার জন্য ধন্যবাদ, এবং দুঃখিত যে আমি এখনই সম্পাদনা করে তা অন্তর্ভুক্ত করতে ভুলে গেছি।
Tcll
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.