2010-12-12 100 views
8

Tôi cần nhập tệp nhị phân từ Python - nội dung được ký số nguyên 16 bit, số cuối lớn.Đọc toàn bộ tệp nhị phân vào trong Python

Các câu hỏi tràn ngăn xếp sau đây gợi ý cách kéo theo nhiều byte cùng một lúc, nhưng đây có phải là cách mở rộng để đọc toàn bộ tệp không?

Tôi nghĩ để tạo ra một chức năng như:

from numpy import * 
import os 

def readmyfile(filename, bytes=2, endian='>h'): 
    totalBytes = os.path.getsize(filename) 
    values = empty(totalBytes/bytes) 
    with open(filename, 'rb') as f: 
     for i in range(len(values)): 
      values[i] = struct.unpack(endian, f.read(bytes))[0] 
    return values 

filecontents = readmyfile('filename') 

Nhưng tôi này s khá chậm (tập tin là 165924350 bytes). Có cách nào tốt hơn?

+0

Tôi nghĩ nó chậm vì 'byte = 2'. – khachik

+0

Đọc một tập tin 150mb là sẽ được làm chậm. Bạn mong chờ điều gì? Làm thế nào chậm là nó? – Falmarri

+0

Nó thực sự chỉ khoảng 3,5 phút (theo unix 'time') nhưng tôi có thể đọc nó trong R trong ít hơn một phút bằng cách sử dụng' readBin' – hatmatrix

Trả lời

4

Tôi sẽ đọc trực tiếp cho đến khi EOF (nó có nghĩa là kiểm tra để nhận một chuỗi rỗng), loại bỏ sau đó sự cần thiết phải sử dụng phạm vi() và nhận được.
Cách khác, sử dụng xrange (thay vì range) sẽ cải thiện mọi thứ, đặc biệt là để sử dụng bộ nhớ.
Hơn nữa, như Falmarri đã đề xuất, đọc nhiều dữ liệu hơn cùng một lúc sẽ cải thiện hiệu suất khá nhiều.

Điều đó nói rằng, tôi sẽ không mong đợi phép lạ, cũng bởi vì tôi không chắc chắn một danh sách là cách hiệu quả nhất để lưu trữ tất cả số lượng dữ liệu đó.
Điều gì về việc sử dụng Mảng của NumPy và các cơ sở của nó cho read/write binary files? Trong link có một phần về đọc các tệp nhị phân thô, sử dụng numpyio.fread. Tôi tin rằng điều này nên được chính xác những gì bạn cần.

Lưu ý: cá nhân, tôi chưa bao giờ sử dụng NumPy; tuy nhiên, raison chính của nó là chính xác xử lý các bộ dữ liệu lớn - và đây là những gì bạn đang làm trong câu hỏi của bạn.

+0

Tôi sẽ xem xét NumPy, nhưng làm cách nào để phân tích cú pháp khi nó được tải? Hoặc phân tích cú pháp trong vòng lặp? Thanks ~ – hatmatrix

+0

Trong liên kết được cung cấp ở trên có một phần về đọc các tệp nhị phân, sử dụng fread. Tôi sẽ cập nhật câu trả lời gốc, để xác định rõ hơn điều này. –

+0

Cảm ơn - liên kết đó hữu ích. – hatmatrix

2

Bạn đang đọc và giải nén 2 byte tại một thời điểm

values[i] = struct.unpack(endian,f.read(bytes))[0] 

Tại sao bạn không đọc như, 1024 byte tại một thời điểm?

+0

Nếu tôi làm điều đó, làm thế nào nó sẽ biết rằng đó là một số nguyên 16-bit chứ không phải là 32 hoặc cái gì khác? – hatmatrix

+0

... và nó cho tôi một lỗi: 'LoạiError: Struct() đối số 1 phải là chuỗi, không int' – hatmatrix

+0

Tôi không nghĩ struct.unpack là giải pháp tốt nhất ở đây. Điều đó có nghĩa là để có trong chuỗi, không phải tập tin nhị phân. – Falmarri

9

Sử dụng numpy.fromfile.

+1

là dễ dàng như 'fromfile (tên tệp, dtype = '> i2')'? – hatmatrix

+0

Phải là; nhưng đọc tài liệu. –

+1

@Stephen, vâng, đó là tất cả những gì bạn cần làm. Nếu Karl đã đặt câu trả lời đó, thì đây là câu trả lời tốt nhất và đơn giản nhất cho việc này. –

1

Tôi nghĩ rằng nút cổ chai bạn có ở đây là gấp đôi.

Tùy thuộc vào hệ điều hành và bộ điều khiển đĩa của bạn, các cuộc gọi tới f.read(2) với f là một tệp lớn thường được đệm hiệu quả - usually. Nói cách khác, hệ điều hành sẽ đọc một hoặc hai sector (với các sector disc thường là vài KB) ra đĩa vào bộ nhớ vì nó không đắt hơn nhiều so với việc đọc 2 byte từ tập tin đó. Các byte thừa được lưu trữ hiệu quả trong bộ nhớ đã sẵn sàng cho cuộc gọi tiếp theo để đọc tệp đó. Đừng dựa vào hành vi đó - nó có thể là nút cổ chai của bạn - nhưng tôi nghĩ có những vấn đề khác ở đây.

Tôi quan tâm nhiều hơn đến việc chuyển đổi một byte thành một cuộc gọi ngắn và đơn lẻ thành số lượng lớn. Đây là những không được lưu trữ ở tất cả. Bạn có thể giữ tất cả các quần short trong một danh sách Python của ints và chuyển đổi toàn bộ danh sách thành gumpy khi (và nếu) cần thiết. Bạn cũng có thể thực hiện một cuộc gọi duy nhất struct.unpack_from để chuyển đổi mọi thứ trong bộ đệm so với một bộ đệm tại một thời điểm.

xem xét:

#!/usr/bin/python 

import random 
import os 
import struct 
import numpy 
import ctypes 

def read_wopper(filename,bytes=2,endian='>h'): 
    buf_size=1024*2 
    buf=ctypes.create_string_buffer(buf_size) 
    new_buf=[] 

    with open(filename,'rb') as f: 
     while True: 
      st=f.read(buf_size) 
      l=len(st) 
      if l==0: 
       break 
      fmt=endian[0]+str(l/bytes)+endian[1]  
      new_buf+=(struct.unpack_from(fmt,st)) 

    na=numpy.array(new_buf)   
    return na 

fn='bigintfile' 

def createmyfile(filename): 
    bytes=165924350 
    endian='>h' 
    f=open(filename,"wb") 
    count=0 

    try: 
     for int in range(0,bytes/2): 
      # The first 32,767 values are [0,1,2..0x7FFF] 
      # to allow testing the read values with new_buf[value<0x7FFF] 
      value=count if count<0x7FFF else random.randint(-32767,32767) 
      count+=1 
      f.write(struct.pack(endian,value&0x7FFF)) 

    except IOError: 
     print "file error" 

    finally: 
     f.close() 

if not os.path.exists(fn): 
    print "creating file, don't count this..." 
    createmyfile(fn) 
else:  
    read_wopper(fn) 
    print "Done!" 

Tôi tạo ra một tập tin của quần short ngẫu nhiên ký ints của 165.924.350 byte (158,24 MB) mà comports đến 82.962.175 ký 2 byte quần short. Với tập tin này, tôi chạy read_wopper chức năng trên và nó chạy trong:

real  0m15.846s 
user  0m12.416s 
sys   0m3.426s 

Nếu bạn không cần quần short được NumPy, chức năng này chạy trong 6 giây. Tất cả điều này trên OS X, python 2.6.1 64 bit, 2.93 gHz Core i7, 8 GB ram. Nếu bạn thay đổi buf_size=1024*2 trong read_wopper-buf_size=2**16 thời gian chạy là:

real  0m10.810s 
user  0m10.156s 
sys   0m0.651s 

Vì vậy, cổ chai chính của bạn, tôi nghĩ, là byte đơn kêu gọi để giải nén - không 2 byte của bạn đọc từ đĩa. Bạn có thể muốn đảm bảo rằng các tệp dữ liệu của bạn không bị phân mảnh và nếu bạn đang sử dụng OS X mà free disc space (và here) của bạn không bị phân mảnh.

Chỉnh sửa Tôi đã đăng mã đầy đủ để tạo sau đó đọc tệp nhị phân của int. Trên iMac của tôi, tôi liên tục nhận được < 15 giây để đọc tệp ints ngẫu nhiên. Phải mất khoảng 1:23 để tạo ra kể từ khi sáng tạo là một đoạn ngắn tại một thời điểm.

+0

Cảm ơn - sẽ thử điều này vào ngày mai, mặc dù hiện đang sử dụng numpy.filefrom - nhưng điều này có thể là tuyệt vời cho các máy không cài đặt khó khăn (điều này không quan trọng đối với tất cả các máy khác nhau mà tôi quản lý)! – hatmatrix

+0

Hmm ... vẫn là 2m50s (OS X, Python 2.6 64-bit, 4GB RAM) ... nhờ có thông tin chi tiết về bộ nhớ cache tho '~ – hatmatrix

+0

@Stephen: Có điều gì bất thường về đĩa của bạn không? Đĩa định dạng NTFS hay thực sự đầy đủ hay bị phân mảnh? Nếu nó là NTFS, trình điều khiển NTFS OS X không phải là nhanh. Tôi sẽ gửi mã đầy đủ của tôi, và thử nó trên một ổ đĩa HFS tương đối trống ... –

2

Tôi đã có cùng một loại vấn đề, mặc dù trong trường hợp cụ thể của tôi, tôi phải chuyển đổi một định dạng nhị phân rất lạ (500   MB) với các khối xen kẽ 166 phần tử là số nguyên được ký 3 byte; vì vậy tôi cũng đã có vấn đề chuyển đổi từ số nguyên 24-bit thành 32 bit đã ký làm chậm một chút.

Tôi đã giải quyết nó bằng cách sử dụng bản ghi nhớ của NumPy (nó chỉ là một cách tiện dụng khi sử dụng bản ghi nhớ của Python) và struct.unpack trên đoạn lớn của tệp.

Với giải pháp này, tôi có thể chuyển đổi (đọc, làm công cụ và ghi vào đĩa) toàn bộ tệp trong khoảng 90   giây (được hẹn giờ với time.clock()).

Tôi có thể tải lên một phần mã.

Các vấn đề liên quan