点击查看代码
# 读取嵌套型和大小可变的二进制结构
from itertools import chain
import struct
# 多边形数组
polys = [
[(1.0, 2.1), (2.0, 3.2), (3.0, 4.3)],
[(1.1, 2.2), (2.1, 3.3), (3.1, 4.4), (4.1, 5.5)],
[(1.2, 2.3), (2.2, 3.4), (3.1, 4.5)],
]
# 文件格式
"""
文件头
字节 类型 描述
0 int 文件代码(0x1234, 小端)
4 double x的最小值(小端)
12 double y的最小值(小端)
20 double x的最大值(小端)
28 double y的最大值(小端)
36 int 多边形数量
正文
字节 类型 描述
0 int 记录长度(N字节)
4-N Points (X,Y)坐标,以浮点数表示
"""
def write_ploys(filename, polys):
flattened = list(chain(*polys))
min_x = min(x for x, y in flattened)
min_y = min(y for x, y in flattened)
max_x = max(x for x, y in flattened)
max_x = max(x for x, y in flattened)
with open(filename, "wb") as f:
head = struct.pack("<iddddi", 0x1234, min_x, min_y, max_x, max_x, len(polys))
f.write(head)
for ploy in polys:
size = len(ploy) * struct.calcsize("<dd")
f.write(struct.pack("<i", size + 4))
for pt in ploy:
f.write(struct.pack("<dd", *pt))
write_ploys("binary_complex", polys)
def read_polys(filename):
with open(filename, "rb") as f:
head = f.read(40)
file_code, min_x, min_y, max_x, max_y, num_polys = struct.unpack(
"<iddddi", head
)
print(
"read file head: ", file_code, min_x, min_y, max_x, max_y, num_polys
) # read file head: 4660 1.0 2.1 4.1 4.1 3
polys = []
for i in range(num_polys):
poly = []
(pbytes,) = struct.unpack("<i", f.read(4))
for j in range(pbytes // 16):
poly.append(struct.unpack("<dd", f.read(16)))
polys.append(poly)
print(
"read polys: ", polys
) # read polys: [[(1.0, 2.1), (2.0, 3.2), (3.0, 4.3)],
# [(1.1, 2.2), (2.1, 3.3), (3.1, 4.4), (4.1, 5.5)],
# [(1.2, 2.3), (2.2, 3.4), (3.1, 4.5)]]
read_polys("binary_complex")
# 上面的代码比较乱 对上面代码的优化
print("-" * 10)
class StructField:
"""Descriptor representing a simple struct field"""
def __init__(self, format, offset):
self.format = format
self.offset = offset
def __get__(self, instance, cls):
if instance is None:
return self
r = struct.unpack_from(self.format, instance._buffer, self.offset)
return r[0] if len(r) == 1 else r
class Structure:
def __init__(self, bytedata):
self._buffer = memoryview(bytedata)
class PolyHead(Structure):
file_code = StructField("<i", 0)
min_x = StructField("<d", 4)
min_y = StructField("<d", 12)
max_x = StructField("<d", 20)
max_y = StructField("<d", 28)
num_polys = StructField("<i", 36)
with open("binary_complex", "rb") as f:
phead = PolyHead(f.read())
print("phead hex file_code: ", hex(phead.file_code))
print("phead min_x: ", phead.min_x)
print("phead num_polys", phead.num_polys)
print("-" * 10)
# 上边这么写很有趣, 但这种方法还有问题,代码比较冗长,重复使用StructField, 要指定偏移量等
# 任何时候,面对冗长的类定义的时候,应考虑用类装饰器或者元类进行优化
class StructureMeta(type):
"""Metaclass that automatically creates StructFiled descriptors"""
def __init__(self, clsname, bases, clsdict):
fields = getattr(self, "_fields_", [])
byte_order = ""
offset = 0
for format, fieldname in fields:
if format.startswith(("<", ">", "!", "@")):
byte_order = format[0]
format = format[1:]
format = byte_order + format
setattr(self, fieldname, StructField(format, offset))
offset += struct.calcsize(format)
setattr(self, "struct_size", offset)
class StructurePro(metaclass=StructureMeta):
def __init__(self, bytedata):
self._buffer = bytedata
@classmethod
def from_file(cls, f):
return cls(f.read(cls.struct_size))
class PolyHeaderPro(StructurePro):
_fields_ = [
("<i", "file_code"),
("d", "min_x"),
("d", "min_y"),
("d", "max_x"),
("d", "max_y"),
("i", "num_polys"),
]
with open("binary_complex", "rb") as f:
phead_pro = PolyHeaderPro.from_file(f)
# phead_pro = PolyHeaderPro(f.read())
print("phead_pro hex file_code: ", hex(phead_pro.file_code))
print("phead_pro min_x: ", phead_pro.min_x)
print("phead_pro num_polys", phead_pro.num_polys)