
سطح مقدماتی (Beginner Level)
در بسیاری از پروژههای پایتون، شما با دادههای سطح بالا (High-level) مانند لیستها، دیکشنریها و رشتهها کار میکنید. اما زمانی که نیاز دارید با فایلهای باینری، پروتکلهای شبکه یا کتابخانههای زبان C ارتباط برقرار کنید، باید دادهها را به بایت (Bytes) تبدیل کنید. ماژول struct در پایتون دقیقاً برای همین کار ساخته شده است.
۱. مفهوم Pack و Unpack
دو عملیات اصلی در این ماژول وجود دارد:
- Packing: تبدیل متغیرهای پایتون (مانند اعداد صحیح و اعشاری) به رشتهای از بایتها.
- Unpacking: تبدیل رشتهای از بایتها به متغیرهای پایتون.
برای انجام این کارها از "رشتههای فرمت" (Format Strings) استفاده میکنیم که به پایتون میگویند هر داده چه نوعی دارد (مثلاً i برای عدد صحیح، f برای اعشاری).
مثال اول: تبدیل عدد به بایت (Pack)
در این مثال ساده، سه عدد را به یک ساختار باینری تبدیل میکنیم.
مثال دوم: بازیابی دادهها (Unpack)
حالا همان دادههای باینری را به متغیرهای پایتون برمیگردانیم.
مثال سوم: تعریف تابع کمکی (Static)
گاهی اوقات میخواهیم تابعی داشته باشیم که فقط عملیات تبدیل را تعریف کند اما فعلاً اجرا نشود.
import struct
def create_binary_packet(player_id, score):
# 'I' به معنی unsigned int و 'f' به معنی float است
return struct.pack('If', player_id, score)
۲. کاراکترهای فرمت (Format Characters)
برای اینکه به struct بگوییم چگونه دادهها را ذخیره کند، باید از کاراکترهای خاصی استفاده کنیم. جدول زیر پرکاربردترینها را نشان میدهد:
x: بایت خالی (Padding)c: کاراکتر (char) - طول ۱ بایتb: عدد صحیح علامتدار (signed char) - طول ۱ بایتh: عدد صحیح کوتاه (short) - طول ۲ بایتi: عدد صحیح (int) - طول ۴ بایتf: عدد اعشاری (float) - طول ۴ بایتd: عدد اعشاری دقیق (double) - طول ۸ بایتs: رشته (char[])
مثال اول: ترکیب انواع مختلف داده
ترکیبی از یک عدد صحیح، دو کاراکتر و یک عدد اعشاری.
مثال دوم: محاسبه سایز فرمت
قبل از تبدیل، میتوانیم بفهمیم یک فرمت خاص چند بایت فضا اشغال میکند.
۳. ترتیب بایتها (Byte Order / Endianness)
کامپیوترهای مختلف بایتها را به ترتیبهای متفاوتی در حافظه ذخیره میکنند (Little Endian vs Big Endian). برای تبادل داده بین سیستمهای مختلف (مثلاً شبکه)، باید این ترتیب را مشخص کنید.
@: ترتیب بومی سیستم (پیشفرض)=: ترتیب استاندارد (بدون Alignment خاص)<: Little Endian (استاندارد PC های اینتل)>: Big Endian (استاندارد شبکه)!: Network (همان Big Endian)
مثال: تفاوت خروجی در Endianness مختلف
سطح پیشرفته (Professional Level)
در سطح حرفهای، بحث کارایی (Performance)، مدیریت بافرها و تراز حافظه (Memory Alignment) اهمیت پیدا میکند. استفاده صحیح از ماژول struct میتواند سرعت پردازش فایلهای حجیم یا بستههای شبکه را به شدت افزایش دهد.
۱. استفاده از کلاس struct.Struct
استفاده از توابع ماژولار مثل struct.pack() برای هر بار صدا زدن، رشته فرمت را کامپایل میکند. اگر قرار است هزاران بار یک فرمت ثابت را تبدیل کنید، بهتر است یک آبجکت Struct بسازید تا فرمت فقط یکبار کامپایل شود. این کار سربار (Overhead) را کاهش میدهد.
مثال اول: مقایسه روش تابعی و کلاسمحور
در اینجا نحوه تعریف کلاس Struct را میبینیم.
مثال دوم: تعریف کلاس پکت (Static)
این الگو در برنامهنویسی شبکه بسیار رایج است.
import struct
class NetworkPacket:
# هدر بسته: 2 بایت نسخه، 2 بایت نوع پیام، 4 بایت طول پیام
_header_struct = struct.Struct('!HH I')
def __init__(self, version, msg_type, payload):
self.version = version
self.msg_type = msg_type
self.payload = payload # Assume bytes
def to_bytes(self):
header = self._header_struct.pack(self.version, self.msg_type, len(self.payload))
return header + self.payload
۲. کار با بافرها (pack_into و unpack_from)
توابع pack و unpack معمولی، رشتههای بایت جدیدی میسازند (Memory allocation). در برنامههای High-Performance، ما میخواهیم روی یک بافر حافظه از پیش تخصیص داده شده بنویسیم تا از ساخت اشیاء اضافی جلوگیری کنیم.
مثال اول: نوشتن در یک بافر مشخص
استفاده از pack_into برای نوشتن داده در آفست خاصی از یک bytearray.
مثال دوم: خواندن از آفستهای مختلف
استفاده از unpack_from برای خواندن بدون برش (Slicing) بافر.
۳. تراز حافظه (Memory Alignment) و Padding
در زبان C، کامپایلرها برای افزایش سرعت دسترسی به حافظه، متغیرها را در آدرسهای زوج (مثلاً مضرب ۴ یا ۸) قرار میدهند. این باعث ایجاد فضاهای خالی یا Padding بین دادهها میشود.
- اگر از
@(Native) استفاده کنید، پایتون Padding اضافه میکند. - اگر از
=(Standard) استفاده کنید، پایتون دادهها را فشرده و بدون Padding میچیند.
مثال اول: مشاهده تفاوت سایز با و بدون Padding
مثال دوم: مدیریت دستی Padding
گاهی نیاز دارید خودتان با کاراکتر x پدینگ را کنترل کنید.
۴. مدیریت رشتههای با طول متغیر (Pascal String vs C-String)
در باینری، رشتهها یا طول ثابت دارند (s) یا طولشان در ابتدای آنها ذکر میشود (Pascal String - p).
مثال: کار با رشتههای ثابت و Pascal
توجه کنید که 10s یعنی یک رشته که دقیقاً ۱۰ بایت فضا میگیرد. اگر رشته کوتاهتر باشد، با null پر میشود.