在這裏插入圖片描述

Python 二進制文件讀寫與解析詳解

一、二進制文件基礎

1. 為什麼需要二進制文件?

  • 效率:二進制文件比文本文件更緊湊,讀寫更快
  • 結構:存儲結構化數據(如圖像、音頻、數據庫文件)
  • 精度:直接存儲原始二進制數據,無編碼轉換

2. 基本操作模式

# 基本打開模式
with open('file.bin', 'rb') as f:  # 讀取二進制
    pass

with open('file.bin', 'wb') as f:  # 寫入二進制
    pass

with open('file.bin', 'ab') as f:  # 追加二進制
    pass

with open('file.bin', 'rb+') as f:  # 讀寫二進制
    pass

二、低級二進制讀寫(struct模塊)

1. struct模塊基礎

import struct

# 打包數據
packed_data = struct.pack('i', 42)  # 4字節整數
print(packed_data)  # b'*\x00\x00\x00'

# 解包數據
value = struct.unpack('i', packed_data)[0]
print(value)  # 42

2. 格式字符串語法

格式字符:
  c: char (1字節)
  b: signed char (1字節)
  B: unsigned char (1字節)
  h: short (2字節)
  H: unsigned short (2字節)
  i: int (4字節)
  I: unsigned int (4字節)
  l: long (4字節)
  L: unsigned long (4字節)
  q: long long (8字節)
  Q: unsigned long long (8字節)
  f: float (4字節)
  d: double (8字節)
  s: char[] (字符串)
  p: char[] (pascal字符串)
  ?: bool (1字節)

3. 複雜結構打包

# 打包多個數據
data = struct.pack('i10sf', 123, b'hello', 3.14)

# 解包
id_num, name, value = struct.unpack('i10sf', data)
name = name.rstrip(b'\x00').decode()  # 清理空字節並解碼

# 使用calcsize計算大小
size = struct.calcsize('i10sf')  # 4 + 10 + 4 = 18字節

三、高級二進制讀寫

1. 使用bytes和bytearray

# 創建字節數據
data = bytes([0x48, 0x65, 0x6C, 0x6C, 0x6F])  # "Hello"

# 讀取二進制文件
with open('data.bin', 'rb') as f:
    # 讀取指定字節數
    chunk = f.read(1024)
    
    # 讀取整個文件
    data = f.read()
    
    # 讀取一行(直到換行符)
    line = f.readline()
    
    # 按字節讀取
    byte = f.read(1)
    while byte:
        # 處理字節
        byte = f.read(1)

# 寫入二進制文件
with open('output.bin', 'wb') as f:
    f.write(b'\x00\x01\x02\x03')
    f.write(bytes([255, 128, 64]))

2. 內存視圖(memoryview)

# 高效處理大文件
with open('large.bin', 'rb') as f:
    # 內存映射文件
    mv = memoryview(f.read())
    
    # 切片操作不復制數據
    header = mv[0:100]
    data_chunk = mv[100:1100]

四、實際應用示例

1. 解析BMP文件頭

def parse_bmp_header(filename):
    with open(filename, 'rb') as f:
        # 讀取文件頭(14字節)
        file_header = f.read(14)
        
        # 解析BMP文件頭
        signature, file_size, reserved, data_offset = \
            struct.unpack('<2sIHHI', file_header)
        
        print(f"文件簽名: {signature}")
        print(f"文件大小: {file_size} 字節")
        print(f"數據偏移: {data_offset} 字節")
        
        # 讀取DIB頭(至少40字節)
        dib_header = f.read(40)
        
        # 解析DIB頭
        header_size, width, height, planes, bits_per_pixel, \
        compression, image_size, x_ppm, y_ppm, colors_used, \
        important_colors = struct.unpack('<IiiHHIIiiII', dib_header)
        
        print(f"圖像尺寸: {width} x {height}")
        print(f"位深度: {bits_per_pixel}")
        print(f"壓縮方式: {compression}")

2. 讀取PNG文件結構

def read_png_chunks(filename):
    PNG_SIGNATURE = b'\x89PNG\r\n\x1a\n'
    
    with open(filename, 'rb') as f:
        signature = f.read(8)
        if signature != PNG_SIGNATURE:
            raise ValueError("不是有效的PNG文件")
        
        while True:
            # 讀取塊長度(4字節)
            chunk_length_data = f.read(4)
            if not chunk_length_data:
                break
                
            chunk_length = struct.unpack('>I', chunk_length_data)[0]
            
            # 讀取塊類型(4字節)
            chunk_type = f.read(4).decode('ascii')
            
            # 讀取塊數據
            chunk_data = f.read(chunk_length)
            
            # 讀取CRC(4字節)
            crc = f.read(4)
            
            print(f"塊類型: {chunk_type}, 長度: {chunk_length}")
            
            if chunk_type == 'IEND':
                break

3. 自定義二進制數據格式

class BinaryDataFormat:
    def __init__(self):
        self.data = []
    
    def add_record(self, id, name, value):
        """添加記錄"""
        name_bytes = name.encode('utf-8')
        # 格式:ID(4B) + 名字長度(2B) + 名字 + 值(4B)
        record = struct.pack('IH', id, len(name_bytes))
        record += name_bytes
        record += struct.pack('f', value)
        self.data.append(record)
    
    def save(self, filename):
        """保存到文件"""
        with open(filename, 'wb') as f:
            # 寫入記錄數量
            f.write(struct.pack('I', len(self.data)))
            
            # 寫入所有記錄
            for record in self.data:
                f.write(record)
    
    @staticmethod
    def load(filename):
        """從文件加載"""
        with open(filename, 'rb') as f:
            # 讀取記錄數量
            num_records = struct.unpack('I', f.read(4))[0]
            
            records = []
            for _ in range(num_records):
                # 讀取記錄頭
                id, name_len = struct.unpack('IH', f.read(6))
                
                # 讀取名字
                name = f.read(name_len).decode('utf-8')
                
                # 讀取值
                value = struct.unpack('f', f.read(4))[0]
                
                records.append({
                    'id': id,
                    'name': name,
                    'value': value
                })
            
            return records

4. 處理網絡數據包

def parse_ethernet_frame(data):
    """解析以太網幀"""
    if len(data) < 14:
        raise ValueError("數據太短")
    
    # 解析以太網頭部
    dest_mac = ':'.join(f'{b:02x}' for b in data[0:6])
    src_mac = ':'.join(f'{b:02x}' for b in data[6:12])
    eth_type = struct.unpack('>H', data[12:14])[0]
    
    print(f"目標MAC: {dest_mac}")
    print(f"源MAC: {src_mac}")
    print(f"以太網類型: 0x{eth_type:04x}")
    
    # 繼續解析IP頭部等...

五、性能優化技巧

1. 批量讀取與解析

def batch_read_binary(filename, chunk_size=4096, record_size=16):
    """批量讀取和解析二進制記錄"""
    records = []
    
    with open(filename, 'rb') as f:
        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break
            
            # 按記錄大小分割
            for i in range(0, len(chunk), record_size):
                record_data = chunk[i:i+record_size]
                if len(record_data) == record_size:
                    # 解析記錄
                    record = struct.unpack('IIf', record_data)
                    records.append(record)
    
    return records

2. 使用array模塊

import array

# 高效處理數值數組
arr = array.array('f')  # 浮點數數組
arr.fromfile(open('data.bin', 'rb'), 1000)  # 讀取1000個浮點數

# 寫入數組
arr.tofile(open('output.bin', 'wb'))

3. 使用numpy處理科學數據

import numpy as np

# 從二進制文件加載數組
data = np.fromfile('data.bin', dtype=np.float32)

# 保存數組到二進制文件
array_data = np.array([1.0, 2.0, 3.0], dtype=np.float32)
array_data.tofile('output.bin')

六、調試與錯誤處理

1. 驗證二進制數據

def validate_binary_data(data, expected_size=None, checksum=None):
    """驗證二進制數據的完整性"""
    
    if expected_size and len(data) != expected_size:
        raise ValueError(f"數據大小錯誤: 期望 {expected_size}, 實際 {len(data)}")
    
    if checksum:
        # 計算簡單的校驗和
        calc_checksum = sum(data) & 0xFF
        if calc_checksum != checksum:
            raise ValueError(f"校驗和錯誤: 期望 {checksum}, 實際 {calc_checksum}")
    
    return True

2. 調試輸出

def hex_dump(data, offset=0, length=None, width=16):
    """生成十六進制轉儲"""
    if length is None:
        length = len(data)
    
    result = []
    for i in range(0, min(length, len(data)), width):
        # 偏移地址
        line = f"{offset + i:08x}: "
        
        # 十六進制
        hex_str = ' '.join(f"{b:02x}" for b in data[i:i+width])
        hex_str = hex_str.ljust(width * 3 - 1)
        
        # ASCII
        ascii_str = ''.join(chr(b) if 32 <= b < 127 else '.' 
                          for b in data[i:i+width])
        
        result.append(f"{line}{hex_str}  {ascii_str}")
    
    return '\n'.join(result)

七、總結與最佳實踐

1. 最佳實踐

  • 明確字節序:使用<(小端)或>(大端)前綴指定字節序
  • 錯誤處理:始終驗證讀取的數據長度
  • 資源管理:使用with語句確保文件正確關閉
  • 性能考慮:對大文件使用緩衝和內存映射

2. 常用工具

  • hexdump模塊:專業的十六進制轉儲
  • construct庫:聲明式二進制數據解析
  • bitstring模塊:位級數據操作
  • pylibelf:ELF文件解析
  • pefile:Windows PE文件解析

3. 完整示例

def binary_file_processor(input_file, output_file):
    """完整的二進制文件處理示例"""
    
    try:
        with open(input_file, 'rb') as infile, \
             open(output_file, 'wb') as outfile:
            
            # 讀取並驗證文件頭
            header = infile.read(4)
            if header != b'DATA':
                raise ValueError("無效的文件格式")
            
            outfile.write(header)
            
            # 處理數據塊
            while True:
                # 讀取塊頭
                chunk_header = infile.read(8)
                if not chunk_header:
                    break
                
                chunk_type, chunk_size = struct.unpack('<II', chunk_header)
                
                # 讀取塊數據
                chunk_data = infile.read(chunk_size)
                if len(chunk_data) != chunk_size:
                    raise ValueError("文件損壞或不完整")
                
                # 處理並寫入數據
                processed_data = process_chunk(chunk_type, chunk_data)
                outfile.write(struct.pack('<II', chunk_type, len(processed_data)))
                outfile.write(processed_data)
            
            print("文件處理完成")
            
    except IOError as e:
        print(f"文件操作錯誤: {e}")
    except struct.error as e:
        print(f"數據解析錯誤: {e}")
    except ValueError as e:
        print(f"數據驗證錯誤: {e}")

通過掌握這些技術,你可以有效地處理各種二進制文件格式,從簡單的數據存儲到複雜的文件格式解析都能遊刃有餘。


結束語 Flutter是一個由Google開發的開源UI工具包,它可以讓您在不同平台上創建高質量、美觀的應用程序,而無需編寫大量平台特定的代碼。我將學習和深入研究Flutter的方方面面。從基礎知識到高級技巧,從UI設計到性能優化,歡飲關注一起討論學習,共同進入Flutter的精彩世界!