การอ่านไฟล์ไบนารีด้วย python


104

ฉันพบว่าไฟล์ไบนารีที่อ่านยากโดยเฉพาะด้วย Python คุณช่วยฉันหน่อยได้ไหม? ฉันต้องการอ่านไฟล์นี้ซึ่งใน Fortran 90 สามารถอ่านได้อย่างง่ายดายโดย

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)

โดยละเอียดรูปแบบไฟล์คือ:

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. 

ฉันจะอ่านสิ่งนี้ด้วย Python ได้อย่างไร ฉันลองทุกอย่างแล้วแต่มันไม่ได้ผล มีโอกาสไหมที่ฉันจะใช้โปรแกรม f90 ใน python อ่านไฟล์ไบนารีนี้แล้วบันทึกข้อมูลที่ฉันต้องใช้


1
ไฟล์นี้เขียนโดยโปรแกรม Fortran หรือไม่? ถ้าเป็นเช่นนั้นมันถูกเขียนอย่างไรเนื่องจาก Fortran ตามค่าเริ่มต้นจะเพิ่มข้อมูลเพิ่มเติมก่อนที่แต่ละระเบียนจะเขียนลงในไฟล์ คุณอาจต้องดูแลเรื่องนี้เมื่ออ่านข้อมูล
คริส

1
โปรดเพิกเฉยต่อความคิดเห็นก่อนหน้าของฉัน intergers 8 และ 4 * N เป็นข้อมูลเพิ่มเติมนี้อย่างชัดเจน
คริส

2
ยังดูคำตอบของคำถามที่อ่านไฟล์ไบนารีในหลาม
คริส

fromfileฟังก์ชันของ Numpy ช่วยให้อ่านไฟล์ไบนารีได้ง่าย ผมแนะนำเลย
littleO

... และระวัง endian-nesses ของคุณอยู่เสมอโดยเฉพาะ เมื่อทำการพอร์ตระหว่างคอมพิวเตอร์ของผู้ผลิตรายอื่น
DragonLord

คำตอบ:


156

อ่านเนื้อหาไฟล์ไบนารีดังนี้:

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

จากนั้น "แกะ" ข้อมูลไบนารีโดยใช้struct.unpack :

ไบต์เริ่มต้น: struct.unpack("iiiii", fileContent[:20])

เนื้อหา: ละเว้นไบต์ส่วนหัวและไบต์ต่อท้าย (= 24); ส่วนที่เหลือสร้างร่างกายเพื่อทราบจำนวนไบต์ในร่างกายให้หารจำนวนเต็มด้วย 4 ผลหารที่ได้รับจะคูณด้วยสตริง'i'เพื่อสร้างรูปแบบที่ถูกต้องสำหรับวิธีการแกะ:

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

ไบต์ท้าย: struct.unpack("i", fileContent[-4:])


คุณช่วยดูโพสต์อื่นนี้ได้ไหม stackoverflow.com/questions/8092469/… ... ฉันอ่านไฟล์ไบนารีอื่นอีกครั้ง แต่ในกรณีนี้ฉันไม่ทราบรายละเอียดโครงสร้างไบต์ ตัวอย่างเช่นฉันพบว่าบางครั้งมีจำนวนเต็ม 8 อย่างไรก็ตามด้วย IDL มันง่ายมากที่จะอ่านข้อมูลนี้ ฉันสามารถทำเช่นเดียวกันกับ python ได้หรือไม่?
Brian

โปรดระบุ (ภายในโพสต์อื่นไม่ใช่ที่นี่) ว่าเหตุใดคุณจึงไม่พอใจกับคำตอบและความคิดเห็นที่โพสต์ บางทีคุณควรอัปเดตคำถามเพื่อให้รายละเอียดเพิ่มเติม ... ฉันจะตรวจสอบเมื่อมีการอัปเดต
gecco

ดูคำตอบนี้หากคุณต้องการแปลงถ่าน [] ที่คลายแพ็กเป็นสตริง
PeterM

import struct
เจดับบลิว

23

โดยทั่วไปแล้วผมอยากจะแนะนำให้คุณมองเข้าไปใช้งูใหญ่structโมดูลสำหรับการนี้ struct.unpack()มันเป็นมาตรฐานกับงูหลามและมันควรจะเป็นเรื่องง่ายในการแปลข้อกำหนดคำถามของคุณเป็นที่สตริงการจัดรูปแบบที่เหมาะสมสำหรับ

โปรดทราบว่าหากมีช่องว่างที่ "มองไม่เห็น" ระหว่าง / รอบ ๆ ฟิลด์คุณจะต้องคิดออกและรวมไว้ในการunpack()โทรมิฉะนั้นคุณจะอ่านบิตผิด

การอ่านเนื้อหาของไฟล์เพื่อให้มีบางสิ่งที่ต้องแกะเป็นเรื่องเล็กน้อย:

import struct

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

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

สิ่งนี้จะคลายสองฟิลด์แรกโดยสมมติว่าเริ่มต้นที่จุดเริ่มต้นของไฟล์ (ไม่มีช่องว่างภายในหรือข้อมูลที่ไม่เกี่ยวข้อง) และสมมติว่ามีลำดับไบต์ดั้งเดิม ( @สัญลักษณ์) Iในการจัดรูปแบบสตริงหมายความว่า "ไม่ได้ลงนามจำนวนเต็ม 32 บิต"


โอเค แต่ฉันไม่รู้ด้วยซ้ำว่าจะอ่านไบต์ของไฟล์อย่างไร จากคำถามของฉันฉันจะอ่านไฟล์จากไบต์ 5 ถึง 8 แล้วแปลงผลลัพธ์เป็นจำนวนเต็มได้อย่างไร ขออภัยฉันเพิ่งเริ่มใช้ Python
Brian

14

คุณสามารถใช้numpy.fromfileซึ่งสามารถอ่านข้อมูลจากทั้งข้อความและไฟล์ไบนารี ก่อนอื่นคุณต้องสร้างประเภทข้อมูลซึ่งแสดงถึงรูปแบบไฟล์ของคุณโดยใช้numpy.dtypeจากนั้นอ่านประเภทนี้จากไฟล์โดยใช้numpy.fromfile.


2
พลาดง่ายขนาดนี้! เอกสารบางไปหน่อย ดูreddit.com/r/Python/comments/19q8nt/…สำหรับการสนทนา
หายไป

11

ในการอ่านไฟล์ไบนารีไปยังbytesวัตถุ:

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

ในการสร้างintจากไบต์ 0-3 ของข้อมูล:

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

ในการคลายไฟล์หลายไฟล์intจากข้อมูล:

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

0

ฉันพบว่า Python ขาดเมื่อพูดถึงการอ่านและเขียนไฟล์ไบนารีดังนั้นฉันจึงเขียนโมดูลเล็ก ๆ (สำหรับ Python 3.6+)

ด้วยbinaryfileคุณจะทำสิ่งนี้ (ฉันเดาว่าเพราะฉันไม่รู้จัก 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)

ซึ่งให้ผลลัพธ์ดังนี้:

{
    '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
}

ฉันใช้การข้าม () เพื่อข้ามข้อมูลเพิ่มเติมที่ Fortran เพิ่ม แต่คุณอาจต้องการเพิ่มยูทิลิตี้เพื่อจัดการกับระเบียน Fortran อย่างถูกต้องแทน หากคุณเป็นเช่นนั้นคำขอดึงจะยินดีต้อนรับ


-2
import pickle
f=open("filename.dat","rb")
try:
    while True:
        x=pickle.load(f)
        print x
except EOFError:
    pass
f.close()

7
อาจเป็นเพียงคำอธิบายเล็กน้อยว่าเหตุใดจึงดีกว่า (หรืออย่างน้อยก็ดีเท่า) คำตอบอื่น ๆ
ฟิล

2
คุณได้ทดสอบการตรวจสอบแล้วว่าใช้งานได้กับไบนารีที่สร้างขึ้นของ Fortran หรือไม่?
ตัวแทน

1
และอธิบายด้วยว่ามันทำอะไร ... ของดองคืออะไร? สิ่งที่ไม่pickle.loadโหลด? มันโหลดสตรีม Fortran ไฟล์โดยตรงหรือตามลำดับหรือไม่ พวกเขาแตกต่างกันและไม่เข้ากัน
Vladimir F
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.