복붙노트

[PYTHON] 압축 및 가변 길이 배열 / 파이썬에서 struct 모듈을 사용하여 문자열

PYTHON

압축 및 가변 길이 배열 / 파이썬에서 struct 모듈을 사용하여 문자열

나는 파이썬 3에서 바이너리 데이터의 패킹과 풀기에 대해 이해하려고 노력하고있다. 하나의 문제를 제외하고는 이해하기 힘들지 않다.

가변 길이의 텍스트 문자열을 가지고 가장 우아한 방식으로 압축하고 풀고 싶다면 어떻게해야할까요?

제가 매뉴얼에서 알 수있는 한 고정 된 크기의 문자열 만 직접 풀 수 있습니까? 이 경우 많은 불필요한 0을 채우지 않고도이 제한을 해결할 수있는 우아한 방법이 있습니까?

해결법

  1. ==============================

    1.구조체 모듈은 고정 길이 구조 만 지원합니다. 가변 길이 문자열의 경우 옵션은 다음 중 하나입니다.

    구조체 모듈은 고정 길이 구조 만 지원합니다. 가변 길이 문자열의 경우 옵션은 다음 중 하나입니다.

    포장을 풀려면 한 번에 조금씩 풀어야합니다.

    (i,), data = struct.unpack("I", data[:4]), data[4:]
    s, data = data[:i], data[i:]
    

    이 작업을 많이 수행하는 경우 언제든지 calcsize를 사용하여 문자열 분할을 수행하는 도우미 함수를 추가 할 수 있습니다.

    def unpack_helper(fmt, data):
        size = struct.calcsize(fmt)
        return struct.unpack(fmt, data[:size]), data[size:]
    
  2. ==============================

    2.다음은 필자가 쓴 몇 가지 래퍼 함수이다.

    다음은 필자가 쓴 몇 가지 래퍼 함수이다.

    포장 풀기 도우미는 다음과 같습니다.

    def unpack_from(fmt, data, offset = 0):
        (byte_order, fmt, args) = (fmt[0], fmt[1:], ()) if fmt and fmt[0] in ('@', '=', '<', '>', '!') else ('@', fmt, ())
        fmt = filter(None, re.sub("p", "\tp\t",  fmt).split('\t'))
        for sub_fmt in fmt:
            if sub_fmt == 'p':
                (str_len,) = struct.unpack_from('B', data, offset)
                sub_fmt = str(str_len + 1) + 'p'
                sub_size = str_len + 1
            else:
                sub_fmt = byte_order + sub_fmt
                sub_size = struct.calcsize(sub_fmt)
            args += struct.unpack_from(sub_fmt, data, offset)
            offset += sub_size
        return args
    

    다음은 패킹 도우미입니다.

    def pack(fmt, *args):
        (byte_order, fmt, data) = (fmt[0], fmt[1:], '') if fmt and fmt[0] in ('@', '=', '<', '>', '!') else ('@', fmt, '')
        fmt = filter(None, re.sub("p", "\tp\t",  fmt).split('\t'))
        for sub_fmt in fmt:
            if sub_fmt == 'p':
                (sub_args, args) = ((args[0],), args[1:]) if len(args) > 1 else ((args[0],), [])
                sub_fmt = str(len(sub_args[0]) + 1) + 'p'
            else:
                (sub_args, args) = (args[:len(sub_fmt)], args[len(sub_fmt):])
                sub_fmt = byte_order + sub_fmt
            data += struct.pack(sub_fmt, *sub_args)
        return data
    
  3. ==============================

    3.나는이 질문과 몇 가지 해결책을 봤다.

    나는이 질문과 몇 가지 해결책을 봤다.

    정교하고 유연한 솔루션.

    from construct import *
    
    PascalString = Struct("PascalString",
        UBInt8("length"),
        Bytes("data", lambda ctx: ctx.length),
    )
    
    >>> PascalString.parse("\x05helloXXX")
    Container({'length': 5, 'data': 'hello'})
    >>> PascalString.build(Container(length = 6, data = "foobar"))
    '\x06foobar'
    
    
    PascalString2 = ExprAdapter(PascalString,
        encoder = lambda obj, ctx: Container(length = len(obj), data = obj),
        decoder = lambda obj, ctx: obj.data
    )
    
    >>> PascalString2.parse("\x05hello")
    'hello'
    >>> PascalString2.build("i'm a long string")
    "\x11i'm a long string"
    

    가변 길이 바이트 시퀀스에 대한 구조체 확장 만 필요하면 빠른 솔루션. 가변 길이 구조의 중첩은 첫 번째 팩 결과를 패킹하여 수행 할 수 있습니다.

    편집 : 가변 길이 문자열의 길이는 요소와 동일한 데이터 형식을 사용하는 것처럼 보입니다. 따라서 단어 길이가 65535 인 경우 가변 길이 문자열의 최대 길이는 255입니다.

    import netstruct
    >>> netstruct.pack(b"b$", b"Hello World!")
    b'\x0cHello World!'
    
    >>> netstruct.unpack(b"b$", b"\x0cHello World!")
    [b'Hello World!']
    
  4. ==============================

    4.팩 사용

    팩 사용

    packed=bytes('sample string','utf-8')
    

    사용을 풀려면

    string=str(packed)[2:][:-1]
    

    이것은 utf-8 문자열에서만 작동하며 아주 간단한 해결 방법입니다.

  5. ==============================

    5.문자열을 패킹 할 때 가변 길이를 수행 할 수있는 쉬운 방법은 다음과 같습니다.

    문자열을 패킹 할 때 가변 길이를 수행 할 수있는 쉬운 방법은 다음과 같습니다.

    pack('{}s'.format(len(string)), string)
    

    포장을 풀 때와 같은 방식입니다.

    unpack('{}s'.format(len(data)), data)
    
  6. ==============================

    6.좋지만 BBBBBB의 경우 '6B'와 같이 숫자 필드 수를 처리 할 수 ​​없습니다. 해결책은 사용하기 전에 두 함수에서 형식 문자열을 확장하는 것입니다. 나는 이것을 생각해 냈다.

    좋지만 BBBBBB의 경우 '6B'와 같이 숫자 필드 수를 처리 할 수 ​​없습니다. 해결책은 사용하기 전에 두 함수에서 형식 문자열을 확장하는 것입니다. 나는 이것을 생각해 냈다.

    def pack(fmt, *args):
      fmt = re.sub('(\d+)([^\ds])', lambda x: x.group(2) * int(x.group(1)), fmt)
      ...
    

    포장을 풀 때도 마찬가지입니다. 어쩌면 가장 우아하지는 않지만 작동합니다 :)

  7. from https://stackoverflow.com/questions/3753589/packing-and-unpacking-variable-length-array-string-using-the-struct-module-in-py by cc-by-sa and MIT license