2.29. Struct dan data biner¶
Modul struct mengemas nilai Python ke dalam tata letak biner tetap dan mengurai byte kembali menjadi nilai Python. Gunakan modul ini saat bekerja dengan format file biner, protokol jaringan, atau perangkat yang menukar rekaman berukuran tetap.
Dua fungsi mencakup sebagian besar kasus:
struct.pack()-- mengambil nilai Python dan sebuah string format, mengembalikan objekbytesdengan tata letak yang tepat.struct.unpack()-- mengambil string format dan objekbytes, mengembalikan tuple nilai Python.
2.29.1. String format¶
Sebuah string format mencantumkan satu kode per bidang dalam rekaman. Kode-kode tersebut mendeskripsikan ukuran dan interpretasi dari setiap bidang.
int Python tidak memiliki ukuran tetap -- ia tumbuh sesuai nilai yang Anda tetapkan. Format biner memiliki ukuran tetap: setiap bidang bilangan bulat menggunakan jumlah byte yang sudah disepakati. struct mengonversi antara int Python yang tak terbatas dengan representasi berukuran tetap ini.
Lebar bilangan bulat adalah jumlah bit yang digunakannya. Satu byte terdiri dari delapan bit. Kode huruf kecil adalah varian bertanda (signed); kode huruf besar adalah yang tidak bertanda (unsigned, hanya nilai non-negatif):
b/B-- 8-bit (satu byte).-128..127bertanda,0..255tidak bertanda.h/H-- 16-bit (dua byte).-32768..32767bertanda,0..65535tidak bertanda.i/I-- 32-bit (empat byte). Sekitar ±dua miliar bertanda, empat miliar tidak bertanda.q/Q-- 64-bit (delapan byte). Secara efektif tidak terbatas untuk penggunaan sehari-hari.
Pilih lebar yang nyaman mencakup rentang yang Anda harapkan. Mengemas nilai di luar rentang yang dideklarasikan bisa secara diam-diam melakukan wrapping atau memunculkan struct.error, tergantung pada build-nya.
Kode umum lainnya adalah untuk float dan string byte:
f-- float 32-bit (presisi tunggal; sekitar tujuh digit desimal).floatbiasa Python di MicroPython sudah berukuran ini, sehingga mengemasnya ke dalamftidak mengalami kehilangan presisi.d-- float 64-bit (presisi ganda; sekitar lima belas digit desimal). MengemasfloatMicroPython 32-bit ke dalamdmemperlebarnya menjadi delapan byte tetapi tidak menambah presisi.s-- string byte dengan panjang tetap, didahului oleh jumlah byte (8suntuk bidang delapan byte).
2.29.2. Urutan byte¶
Bilangan bulat multi-byte dapat disimpan di memori dengan dua cara. Angka 0x12345678 dalam bidang 32-bit ditata seperti ini:
Little-endian -- byte paling tidak signifikan terlebih dahulu:
78 56 34 12.Big-endian -- byte paling signifikan terlebih dahulu:
12 34 56 78.
Keduanya mengodekan nilai yang sama; perbedaannya hanya pada ujung mana dari bidang yang merupakan byte rendah. File yang ditulis oleh satu sistem akan berantakan saat dibaca oleh sistem lain jika urutan byte tidak cocok.
Karakter pertama dari string format menentukan urutan:
<-- little-endian. Umum pada x86 dan ARM.>-- big-endian. Umum dalam protokol jaringan.!-- urutan jaringan, setara dengan>.
Tanpa karakter awal, urutan byte dan penyelarasan (alignment) native yang digunakan; menetapkan < atau > secara eksplisit menghilangkan ambiguitas tersebut dan biasanya itulah yang Anda inginkan saat membaca file atau berkomunikasi dengan mesin lain.
Catatan
OpenMV Cam bersifat little-endian -- sama seperti PC host-nya. Gunakan < dalam string format untuk file lokal kamera dan untuk data biner yang dikirim ke atau dari desktop. Gunakan > (atau !) untuk protokol jaringan dan untuk format apa pun yang spesifikasinya menggunakan big-endian.
"<HI" mengemas nilai 16-bit diikuti nilai 32-bit ke dalam enam byte little-endian.¶
2.29.3. Pengemasan¶
import struct
blob = struct.pack("<HI", 320, 1000000)
print(blob, len(blob))
Output:
b'@\x01@B\x0f\x00' 6
Format <HI menghasilkan enam byte: dua untuk bidang H dan empat untuk bidang I, semuanya little-endian. Berikan tepat jumlah nilai yang diharapkan format -- ketidakcocokan akan memunculkan struct.error.
2.29.4. Pembongkaran¶
width, count = struct.unpack("<HI", blob)
print(width, count)
Output:
320 1000000
struct.unpack() selalu mengembalikan tuple, bahkan ketika format hanya mendeskripsikan satu bidang. Bongkar hasilnya pada baris yang sama untuk keterbacaan.
2.29.5. String byte dengan panjang tetap¶
Kode s membaca atau menulis sekumpulan byte secara verbatim. Jumlah byte ditulis sebelum s -- 4s berarti "empat byte yang diperlakukan sebagai satu string byte". Ini adalah cara umum untuk menyematkan nilai magic, tag berukuran tetap, atau bidang nama berisi padding dalam sebuah rekaman:
header = struct.pack("<4sHI", b"OMV0", 320, 1000000)
print(header)
Output:
b'OMV0@\x01@B\x0f\x00'
Empat byte pertama adalah magic literal b"OMV0"; dua berikutnya adalah bidang H (320); empat terakhir adalah bidang I (1000000). Pembongkaran mengembalikan byte tersebut sebagai objek bytes:
magic, width, count = struct.unpack("<4sHI", header)
print(magic, width, count)
Output:
b'OMV0' 320 1000000
Jika nilai sumber lebih pendek dari jumlah yang dideklarasikan, hasilnya diisi di sisi kanan dengan \x00; jika lebih panjang, byte yang berlebih akan dihapus secara diam-diam:
struct.pack("4s", b"hi") # b'hi\x00\x00'
struct.pack("4s", b"toolong") # b'tool'
Jumlah tersebut adalah panjang dalam byte, bukan jumlah karakter -- s bekerja dengan byte mentah, sehingga string UTF-8 dengan karakter multi-byte perlu di-.encode() dan dihitung dalam byte terlebih dahulu.
2.29.6. Menghitung ukuran dan pembacaan parsial¶
struct.calcsize() mengembalikan jumlah byte yang dikonsumsi oleh sebuah string format:
struct.calcsize("<HI") # 6
Saat membaca aliran rekaman dari sebuah file, baca tepat sebanyak itu byte per rekaman:
record_size = struct.calcsize("<HI")
with open("data.bin", "rb") as f:
while True:
chunk = f.read(record_size)
if len(chunk) < record_size:
break
width, count = struct.unpack("<HI", chunk)
print(width, count)
Pembacaan singkat di akhir file menghasilkan potongan yang lebih kecil dari record_size -- perlakukan itu sebagai kondisi akhir aliran daripada mencoba membongkar rekaman yang tidak lengkap.