Membaca file biner dengan python

104

Saya merasa sangat sulit membaca file biner dengan Python. Bisakah Anda membantu saya? Saya perlu membaca file ini, yang di Fortran 90 mudah dibaca

int*4 n_particles, n_groups
real*4 group_id(n_particles)
read (*) n_particles, n_groups
read (*) (group_id(j),j=1,n_particles)

Secara detail, format file tersebut adalah:

Bytes 1-4 -- The integer 8.
Bytes 5-8 -- The number of particles, N.
Bytes 9-12 -- The number of groups.
Bytes 13-16 -- The integer 8.
Bytes 17-20 -- The integer 4*N.
Next many bytes -- The group ID numbers for all the particles.
Last 4 bytes -- The integer 4*N. 

Bagaimana saya bisa membaca ini dengan Python? Saya mencoba segalanya tetapi tidak pernah berhasil. Apakah ada kemungkinan saya menggunakan program f90 dengan python, membaca file biner ini dan kemudian menyimpan data yang perlu saya gunakan?

Brian
sumber
1
Apakah file ini ditulis oleh program Fortran? Jika demikian, bagaimana itu ditulis, karena Fortran, secara default, menambahkan data tambahan sebelum setiap record ditulis ke file. Anda mungkin perlu berhati-hati saat membaca data.
Chris
1
Abaikan komentar saya sebelumnya, intergers 8 dan 4 * N jelas merupakan data tambahan ini.
Chris
2
Juga, lihat jawaban atas pertanyaan membaca file biner dengan python .
Chris
Fungsi Numpy fromfilememudahkan untuk membaca file biner. Saya merekomendasikannya.
littleO
... dan selalu hati-hati dengan endian-nesses Anda, khususnya. saat melakukan port antara komputer pabrikan yang berbeda.
DragonLord

Jawaban:

155

Baca konten file biner seperti ini:

with open(fileName, mode='rb') as file: # b is important -> binary
    fileContent = file.read()

lalu " keluarkan " data biner menggunakan struct.unpack :

Byte awal: struct.unpack("iiiii", fileContent[:20])

Body: abaikan byte heading dan byte trailing (= 24); Bagian yang tersisa membentuk body, untuk mengetahui jumlah byte pada body lakukan pembagian integer sebanyak 4; Hasil bagi yang diperoleh dikalikan dengan string 'i'untuk membuat format yang benar untuk metode unpack:

struct.unpack("i" * ((len(fileContent) -24) // 4), fileContent[20:-4])

Byte akhir: struct.unpack("i", fileContent[-4:])

gecco
sumber
Bisakah Anda melihat posting lain ini? stackoverflow.com/questions/8092469/… ... Sekali lagi saya membaca file biner lain, tetapi dalam kasus ini saya tidak mengetahui struktur byte secara detail. Sebagai contoh, saya menemukan bahwa terkadang ada bilangan bulat 8. Namun, dengan IDL sangat mudah untuk membaca data ini. Bisakah saya melakukan hal yang sama dengan python?
Brian
Tolong tunjukkan (di dalam posting lain, bukan di sini) mengapa Anda tidak senang dengan jawaban dan komentar yang diposting. Mungkin Anda juga harus memperbarui pertanyaan untuk memberikan rincian lebih lanjut ... Saya akan melihatnya ketika diperbarui.
gecco
Lihat jawaban ini jika Anda perlu mengonversi karakter yang tidak dikemas [] menjadi string.
PeterM
import struct
JW
23

Secara umum, saya akan merekomendasikan Anda untuk menggunakan modul struct Python untuk ini. Ini standar dengan Python, dan seharusnya mudah untuk menerjemahkan spesifikasi pertanyaan Anda ke dalam string pemformatan yang sesuai struct.unpack().

Perhatikan bahwa jika ada padding "tak terlihat" di antara / sekitar bidang, Anda perlu mencari tahu dan memasukkannya ke dalam unpack()panggilan, atau Anda akan membaca bit yang salah.

Membaca konten file untuk mendapatkan sesuatu untuk dibongkar cukup sepele:

import struct

data = open("from_fortran.bin", "rb").read()

(eight, N) = struct.unpack("@II", data)

Ini membongkar dua bidang pertama, dengan asumsi mereka mulai di awal file (tidak ada padding atau data asing), dan juga mengasumsikan urutan byte asli ( @simbol). Huruf Is dalam string pemformatan berarti "unsigned integer, 32 bits".

beristirahat
sumber
ok, tapi saya bahkan tidak tahu cara membaca byte dari file tersebut. Dari pertanyaan saya, bagaimana saya bisa membaca file dari byte 5 hingga 8 dan kemudian mengubah hasilnya menjadi integer? Maaf, tapi saya baru mengenal Python.
Brian
14

Anda dapat menggunakan numpy.fromfile, yang dapat membaca data dari teks dan file biner. Pertama-tama Anda akan membuat tipe data, yang mewakili format file Anda, menggunakan numpy.dtype, lalu membaca tipe ini dari file menggunakan numpy.fromfile.

Chris
sumber
2
Mudah untuk melewatkan ini! Dokumen agak tipis; lihat reddit.com/r/Python/comments/19q8nt/… untuk beberapa diskusi
hilang
11

Untuk membaca file biner menjadi bytesobjek:

from pathlib import Path
data = Path('/path/to/file').read_bytes()  # Python 3.5+

Untuk membuat intdari byte 0-3 data:

i = int.from_bytes(data[:4], byteorder='little', signed=False)

Untuk mengekstrak beberapa intdari data:

import struct
ints = struct.unpack('iiii', data[:16])
Eugene Yarmash
sumber
0

Saya juga menemukan Python kurang dalam hal membaca dan menulis file biner, jadi saya menulis modul kecil (untuk Python 3.6+).

Dengan binaryfile Anda akan melakukan sesuatu seperti ini (saya menduga, karena saya tidak tahu Fortran):

import binaryfile

def particle_file(f):
    f.array('group_ids')  # Declare group_ids to be an array (so we can use it in a loop)
    f.skip(4)  # Bytes 1-4
    num_particles = f.count('num_particles', 'group_ids', 4)  # Bytes 5-8
    f.int('num_groups', 4)  # Bytes 9-12
    f.skip(8)  # Bytes 13-20
    for i in range(num_particles):
        f.struct('group_ids', '>f')  # 4 bytes x num_particles
    f.skip(4)

with open('myfile.bin', 'rb') as fh:
    result = binaryfile.read(fh, particle_file)
print(result)

Yang menghasilkan keluaran seperti ini:

{
    'group_ids': [(1.0,), (0.0,), (2.0,), (0.0,), (1.0,)],
    '__skipped': [b'\x00\x00\x00\x08', b'\x00\x00\x00\x08\x00\x00\x00\x14', b'\x00\x00\x00\x14'],
    'num_particles': 5,
    'num_groups': 3
}

Saya menggunakan skip () untuk melewati data tambahan yang ditambahkan Fortran, tetapi Anda mungkin ingin menambahkan utilitas untuk menangani catatan Fortran dengan benar. Jika Anda melakukannya, permintaan tarik akan diterima.

Fax
sumber
-2
import pickle
f=open("filename.dat","rb")
try:
    while True:
        x=pickle.load(f)
        print x
except EOFError:
    pass
f.close()
Eeshitri
sumber
6
Mungkin berharga hanya sedikit penjelasan tentang mengapa ini lebih baik daripada (atau setidaknya sebaik) jawaban lain.
Phil
2
sudahkah Anda menguji dan memverifikasi bahwa ini berfungsi dengan biner yang dihasilkan fortran?
agentp
1
Dan juga jelaskan apa fungsinya ... Apa itu acar? Apa yang pickle.loaddimuat? Apakah itu memuat aliran Fortran, file langsung atau berurutan? Mereka berbeda dan tidak kompatibel.
Vladimir F