解压以ASCIIZ字符串结尾的结构

2024-05-13 09:22:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图使用struct.unpack()来拆分以ASCII字符串结尾的数据记录。在

记录(正好是tomtomov2记录)具有以下格式(存储的little endian):

  • 1字节
  • 4字节int表示总记录大小(包括此字段)
  • 4字节整数
  • 4字节整数
  • 可变长度字符串,以null结尾

unpack()要求字符串的长度包含在传递给它的格式中。我可以使用第二个字段和记录其余部分的已知大小(13个字节)来获得字符串长度:

str_len = struct.unpack("<xi", record[:5])[0] - 13
fmt = "<biii{0}s".format(str_len)

然后继续进行完整的解包,但是由于字符串是以null结尾的,我真的希望unpack()能帮我完成。如果我在一个不包含自身大小的结构中运行,也会很好。在

我怎么能做到呢?在


Tags: 数据字符串len字节格式结尾记录ascii
2条回答

实际上,由于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)

^{pr2}$

我创建了两个新函数,可以作为标准pack和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)

下面是一个如何使用它们的示例:

^{pr2}$

相关问题 更多 >