我想使用struct.unpack()
拆开与ASCII字符串结尾的数据记录。
记录(这恰好是一个TomTom的OV2记录)具有这种格式(存储小端):
- 1个字节
- 4字节int对于总记录大小(包括本领域)
- 4字节INT
- 4字节INT
- 可变长度的字符串,空终止
unpack()
要求字符串的长度被包含在你通过它的格式。 我可以使用第二个字段和记录的其余部分的已知大小 - 13个字节 - 获取字符串长度:
str_len = struct.unpack("<xi", record[:5])[0] - 13
fmt = "<biii{0}s".format(str_len)
然后用完整的开箱继续,但由于该字符串是空值终止的,我真的希望unpack()
会为我做的。 这也将会是不错的这个应该我碰到不包括其自身的尺寸结构运行。
我怎样才能做到这一点?
我做了两个新的功能,应该是可用作插入式替代标准包并解压功能。 他们都支持的“Z”字收拾/解压缩的ASCIIZ字符串。 还有的位置,或在格式字符串中的“Z”字的出现次数没有限制:
import struct
def unpack (format, buffer) :
while True :
pos = format.find ('z')
if pos < 0 :
break
asciiz_start = struct.calcsize (format[:pos])
asciiz_len = buffer[asciiz_start:].find('\0')
format = '%s%dsx%s' % (format[:pos], asciiz_len, format[pos+1:])
return struct.unpack (format, buffer)
def pack (format, *args) :
new_format = ''
arg_number = 0
for c in format :
if c == 'z' :
new_format += '%ds' % (len(args[arg_number])+1)
arg_number += 1
else :
new_format += c
if c in 'cbB?hHiIlLqQfdspP' :
arg_number += 1
return struct.pack (new_format, *args)
这里有一个如何使用它们的例子:
>>> from struct_z import pack, unpack
>>> line = pack ('<izizi', 1, 'Hello', 2, ' world!', 3)
>>> print line.encode('hex')
0100000048656c6c6f000200000020776f726c64210003000000
>>> print unpack ('<izizi',line)
(1, 'Hello', 2, ' world!', 3)
>>>
大小无记录是很容易处理的,实际上,由于struct.calcsize()
会告诉你,它预计长度。 您可以使用和数据的实际长度,以构建一个新的格式字符串unpack()
其中包括正确的字符串长度。
此功能只是一个包装unpack()
允许在最后的位置,将终端跌落NUL一个新的格式字符:
import struct
def unpack_with_final_asciiz(fmt, dat):
"""
Unpack binary data, handling a null-terminated string at the end
(and only at the end) automatically.
The first argument, fmt, is a struct.unpack() format string with the
following modfications:
If fmt's last character is 'z', the returned string will drop the NUL.
If it is 's' with no length, the string including NUL will be returned.
If it is 's' with a length, behavior is identical to normal unpack().
"""
# Just pass on if no special behavior is required
if fmt[-1] not in ('z', 's') or (fmt[-1] == 's' and fmt[-2].isdigit()):
return struct.unpack(fmt, dat)
# Use format string to get size of contained string and rest of record
non_str_len = struct.calcsize(fmt[:-1])
str_len = len(dat) - non_str_len
# Set up new format string
# If passed 'z', treat terminating NUL as a "pad byte"
if fmt[-1] == 'z':
str_fmt = "{0}sx".format(str_len - 1)
else:
str_fmt = "{0}s".format(str_len)
new_fmt = fmt[:-1] + str_fmt
return struct.unpack(new_fmt, dat)
>>> dat = b'\x02\x1e\x00\x00\x00z\x8eJ\x00\xb1\x7f\x03\x00Down by the river\x00'
>>> unpack_with_final_asciiz("<biiiz", dat)
(2, 30, 4886138, 229297, b'Down by the river')