struct — 바이트를 패킹된 이진 데이터로 해석
struct — 바이트를 패킹된 이진 데이터로 해석
이 모듈은 Python 값과 Python bytes 객체로 표현되는 C struct 사이를 변환합니다. 컴팩트한 포맷 문자열은 Python 값과의 변환을 설명합니다. 모듈의 함수와 객체는 크게 구별되는 두 가지 응용, 즉 외부 소스(파일 또는 네트워크 연결)와의 데이터 교환, 또는 Python 응용 프로그램과 C 계층 사이의 데이터 전송에 사용할 수 있습니다.
본문
Note
접두 문자가 주어지지 않으면 네이티브 모드가 기본값입니다. 이것은 Python 인터프리터가 빌드된 플랫폼과 컴파일러에 따라 데이터를 패킹하거나 언패킹합니다. 주어진 C struct를 패킹한 결과에는 관련된 C 타입에 대해 올바른 정렬을 유지하는 패딩 바이트가 포함됩니다. 마찬가지로 언패킹할 때도 정렬이 고려됩니다. 반면 외부 소스와 데이터를 교환할 때는 프로그래머가 요소 사이의 바이트 순서와 패딩을 정의해야 합니다. 자세한 내용은 바이트 순서, 크기 및 정렬(Byte Order, Size, and Alignment)을 참고하세요.
여러 struct 함수(및 Struct의 메서드)는 buffer 인자를 받습니다. 이것은 버퍼 프로토콜을 구현하고 읽기 가능하거나 읽기-쓰기 가능한 버퍼를 제공하는 객체를 가리킵니다. 이 목적으로 가장 일반적인 유형은 bytes와 bytearray이지만, 바이트 배열로 볼 수 있는 많은 다른 유형이 버퍼 프로토콜을 구현하므로 추가 복사 없이 읽거나 채울 수 있습니다.
함수와 예외
모듈은 다음 예외와 함수를 정의합니다.
struct.error
여러 상황에서 발생하는 예외입니다. 인자는 무엇이 잘못되었는지 설명하는 문자열입니다.
struct.pack(format, v1, v2, ...)
포맷 문자열 format에 따라 값 v1, v2, ...을 패킹한 bytes 객체를 반환합니다. 인자는 포맷이 요구하는 값과 정확히 일치해야 합니다.
struct.pack_into(format, buffer, offset, v1, v2, ...)
포맷 문자열 format에 따라 값 v1, v2, ...을 패킹하고 패킹된 바이트를 위치 offset에서 시작하는 쓰기 가능한 버퍼 buffer에 씁니다. offset은 필수 인자입니다. 음수 오프셋은 buffer의 끝에서부터 계산됩니다.
struct.unpack(format, buffer)
포맷 문자열 format에 따라 버퍼 buffer(아마도 pack(format, ...)으로 패킹된)에서 언패킹합니다. 항목이 정확히 하나여도 결과는 튜플입니다. 버퍼의 바이트 크기는 calcsize()가 반영하는 포맷이 요구하는 크기와 일치해야 합니다.
struct.unpack_from(format, /, buffer, offset=0)
포맷 문자열 format에 따라 위치 offset에서 시작하는 버퍼에서 언패킹합니다. 항목이 정확히 하나여도 결과는 튜플입니다. 위치 offset에서 시작하는 버퍼의 바이트 크기는 calcsize()가 반영하는 포맷이 요구하는 크기 이상이어야 합니다. 음수 오프셋은 buffer의 끝에서부터 계산됩니다.
struct.iter_unpack(format, buffer)
포맷 문자열 format에 따라 버퍼 buffer에서 반복적으로 언패킹합니다. 이 함수는 버퍼의 모든 내용이 소비될 때까지 동일한 크기의 청크를 읽는 이터레이터를 반환합니다. 버퍼의 바이트 크기는 calcsize()가 반영하는 포맷이 요구하는 크기의 배수여야 합니다.
각 반복은 포맷 문자열이 지정하는 튜플을 생성합니다.
versionadded: 3.4.
struct.calcsize(format)
포맷 문자열 format에 해당하는 struct(따라서 pack(format, ...)이 생성하는 bytes 객체)의 크기를 반환합니다.
포맷 문자열
포맷 문자열은 데이터를 패킹하고 언패킹할 때 데이터 레이아웃을 설명합니다. 그것들은 패킹/언패킹되는 데이터의 유형을 지정하는 포맷 문자로 구성됩니다. 또한 특수 문자는 바이트 순서, 크기 및 정렬을 제어합니다.
각 포맷 문자열은 데이터의 전체 속성을 설명하는 선택적 접두 문자와 실제 데이터 값 및 패딩을 설명하는 하나 이상의 포맷 문자로 구성됩니다.
바이트 순서, 크기 및 정렬
기본적으로 C 타입은 머신의 네이티브 포맷과 바이트 순서로 표현되며, 필요한 경우 패딩 바이트를 건너뛰어 올바르게 정렬됩니다(C 컴파일러가 사용하는 규칙에 따라). 이 동작은 패킹된 struct의 바이트가 해당 C struct의 메모리 레이아웃과 정확히 일치하도록 선택되었습니다. 네이티브 바이트 순서와 패딩을 사용할지 표준 포맷을 사용할지는 응용 프로그램에 달려 있습니다.
대안으로 포맷 문자열의 첫 번째 문자는 다음 표에 따라 패킹된 데이터의 바이트 순서, 크기 및 정렬을 나타내는 데 사용할 수 있습니다.
| 문자 | 바이트 순서 | 크기 | 정렬 |
|---|---|---|---|
@ |
native | native | native |
= |
native | standard | none |
< |
little-endian | standard | none |
> |
big-endian | standard | none |
! |
network (= big-endian) | standard | none |
첫 번째 문자가 이 중 하나가 아니면 '@'로 간주됩니다.
Note
숫자 1023(16진수 0x3ff)은 다음 바이트 표현을 가집니다:
- big-endian(
>)에서03ff- little-endian(
<)에서ff03Python 예제:
>>> import struct >>> struct.pack('>h', 1023) b'\x03\xff' >>> struct.pack('<h', 1023) b'\xff\x03'
네이티브 바이트 순서는 호스트 시스템에 따라 big-endian 또는 little-endian입니다. 예를 들어 Intel x86, AMD64(x86-64), Apple M1은 little-endian이고 IBM z와 많은 레거시 아키텍처는 big-endian입니다. 시스템의 엔디안을 확인하려면 sys.byteorder를 사용하세요.
네이티브 크기와 정렬은 C 컴파일러의 sizeof 표현식을 사용하여 결정됩니다. 이것은 항상 네이티브 바이트 순서와 결합됩니다.
표준 크기는 포맷 문자에만 의존합니다. 포맷 문자 섹션의 표를 참고하세요.
'@'와 '='의 차이에 주목하세요: 둘 다 네이티브 바이트 순서를 사용하지만 후자의 크기와 정렬은 표준화되어 있습니다.
'!' 형태는 IETF RFC 1700에서 정의한 대로 항상 big-endian인 네트워크 바이트 순서를 나타냅니다.
비네이티브 바이트 순서(강제 바이트 스와핑)를 나타내는 방법은 없습니다. '<' 또는 '>'의 적절한 선택을 사용하세요.
참고:
- 패딩은 연속적인 구조체 멤버 사이에만 자동으로 추가됩니다. 인코딩된 struct의 시작이나 끝에는 패딩이 추가되지 않습니다.
<,>,=,!와 같은 비네이티브 크기와 정렬을 사용할 때는 패딩이 추가되지 않습니다.- struct의 끝을 특정 타입의 정렬 요구 사항에 맞추려면 반복 횟수가 0인 해당 타입의 코드로 포맷을 끝내세요. 예제 참고.
포맷 문자
포맷 문자는 다음 의미를 가집니다. C와 Python 값 사이의 변환은 그 유형을 감안하면 명확해야 합니다. '표준 크기(Standard size)' 열은 표준 크기를 사용할 때(즉, 포맷 문자열이 <, >, !, = 중 하나로 시작할 때) 패킹된 값의 크기(바이트)를 나타냅니다. 네이티브 크기를 사용할 때 패킹된 값의 크기는 플랫폼에 따라 다릅니다.
| 포맷 | C 타입 | Python 타입 | 표준 크기 | 참고 |
|---|---|---|---|---|
x |
pad byte | 값 없음 | - | (7) |
c |
char | 길이 1의 bytes | 1 | |
b |
signed char | int | 1 | (2) |
B |
unsigned char | int | 1 | (2) |
? |
_Bool | bool | 1 | (1) |
h |
short | int | 2 | (2) |
H |
unsigned short | int | 2 | (2) |
i |
int | int | 4 | (2) |
I |
unsigned int | int | 4 | (2) |
l |
long | int | 4 | (2) |
L |
unsigned long | int | 4 | (2) |
q |
long long | int | 8 | (2) |
Q |
unsigned long long | int | 8 | (2) |
n |
ssize_t | int | - | (2), (3) |
N |
size_t | int | - | (2), (3) |
e |
_Float16 | float | 2 | (4), (6) |
f |
float | float | 4 | (4) |
d |
double | float | 8 | (4) |
F |
float complex | complex | 8 | (10) |
D |
double complex | complex | 16 | (10) |
s |
char[] | bytes | - | (9) |
p |
char[] | bytes | - | (8) |
P |
void* | int | - | (2), (5) |
versionchanged: 3.3에서
'n'과'N'포맷 지원이 추가되었습니다.versionchanged: 3.6에서
'e'포맷 지원이 추가되었습니다.versionchanged: 3.14에서
'F'와'D'포맷 지원이 추가되었습니다.
See also
array와ctypes모듈, 그리고 numpy와 같은 서드파티 모듈은 유사하지만 약간 다른 타입 코드를 사용합니다.
참고:
'?'변환 코드는 C99 이후 C 표준이 정의하는_Bool타입에 해당합니다. 표준 모드에서는 1바이트로 표현됩니다.- 정수 변환 코드 중 하나를 사용하여 정수가 아닌 값을 패킹하려고 할 때, 그 값이
__index__()메서드를 가지면 패킹 전에 그 메서드를 호출하여 인자를 정수로 변환합니다.versionchanged: 3.2에서 정수가 아닌 값에
__index__()메서드 사용이 추가되었습니다. 'n'과'N'변환 코드는 네이티브 크기(기본값 또는'@'바이트 순서 문자로 선택)에서만 사용할 수 있습니다. 표준 크기의 경우 응용 프로그램에 맞는 다른 정수 포맷을 사용할 수 있습니다.'f','d','e'변환 코드의 경우 패킹된 표현은 플랫폼이 사용하는 부동소수점 포맷과 관계없이 각각'f','d','e'에 대해 IEEE 754 binary32, binary64 또는 binary16 포맷을 사용합니다.'P'포맷 문자는 네이티브 바이트 순서(기본값 또는'@'바이트 순서 문자로 선택)에서만 사용할 수 있습니다. 바이트 순서 문자'='는 호스트 시스템에 따라 little- 또는 big-endian 순서를 선택합니다. struct 모듈은 이것을 네이티브 순서로 해석하지 않으므로'P'포맷은 사용할 수 없습니다.- IEEE 754 binary16 "반 정밀도" 타입은 IEEE 754 표준의 2008 개정판에서 도입되었습니다. 부호 비트, 5비트 지수, 11비트 정밀도(10비트는 명시적으로 저장됨)를 가지며 전체 정밀도로 약 6.1e-05에서 6.5e+04 사이의 숫자를 표현할 수 있습니다. 이 타입은 C 컴파일러에서 널리 지원되지 않습니다: 컴파일러가 C23 표준의 Annex H를 지원하면
_Float16타입으로 사용할 수 있습니다.
개수(count)가 주어지지 않으면 기본값 1입니다. 패킹의 경우 바이트 문자열은 맞도록 잘리거나 null 바이트로 패딩됩니다. 언패킹의 경우 결과 bytes 객체는 항상 정확히 지정된 바이트 수를 가집니다. 특수한 경우로 '0s'는 단일 빈 바이트 문자열을 의미합니다('0c'는 0 문자를 의미하는 반면). 패킹할 때 bytes와 bytearray 타입의 인자가 허용됩니다.
'F'와'D'포맷 문자의 경우 패킹된 표현은 플랫폼이 사용하는 부동소수점 포맷과 관계없이 복소수의 구성 요소에 대해 IEEE 754 binary32 및 binary64 포맷을 사용합니다. C에서 복소수 타입이 선택적 기능임에도 불구하고 복소수 타입(F와D)은 무조건 사용 가능합니다. C11 표준에 지정된 대로 각 복소수 타입은 각각 실수부와 허수부를 포함하는 두 요소 C 배열로 표현됩니다.
포맷 문자 앞에는 정수 반복 횟수가 올 수 있습니다. 예를 들어 포맷 문자열 '4h'는 'hhhh'와 정확히 같습니다.
포맷 사이의 공백 문자는 무시됩니다. 그러나 개수와 그 포맷 사이에는 공백이 없어야 합니다.
정수 포맷('b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q') 중 하나를 사용하여 값 x를 패킹할 때, x가 해당 포맷의 유효 범위를 벗어나면 struct.error가 발생합니다.
versionchanged: 3.1에서 이전에는 일부 정수 포맷이 범위를 벗어난 값을 감싸고
struct.error대신DeprecationWarning을 발생시켰습니다.
'?' 포맷 문자의 경우 반환 값은 True 또는 False입니다. 패킹할 때 인자 객체의 진리값이 사용됩니다. 네이티브 또는 표준 bool 표현에서 0 또는 1이 패킹되고, 언패킹할 때 0이 아닌 값은 True가 됩니다.
예제
Note
네이티브 바이트 순서 예제(
'@'포맷 접두 또는 접두 문자가 없는 경우)는 플랫폼과 컴파일러에 의존하므로 독자의 머신이 생성하는 결과와 일치하지 않을 수 있습니다.
big-endian 순서를 사용하여 세 가지 다른 크기의 정수를 패킹하고 언패킹합니다:
>>> from struct import *
>>> pack(">bhl", 1, 2, 3)
b'\x01\x00\x02\x00\x00\x00\x03'
>>> unpack('>bhl', b'\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)
>>> calcsize('>bhl')
7
정의된 필드에 비해 너무 큰 정수를 패킹하려고 시도합니다:
>>> pack(">h", 99999)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
struct.error: 'h' format requires -32768 <= number <= 32767
s와 c 포맷 문자의 차이를 보여줍니다:
>>> pack("@ccc", b'1', b'2', b'3')
b'123'
>>> pack("@3s", b'123')
b'123'
언패킹된 필드는 변수에 할당하거나 결과를 named tuple로 감싸서 이름을 지을 수 있습니다:
>>> record = b'raymond \x32\x12\x08\x01\x08'
>>> name, serialnum, school, gradelevel = unpack('<10sHHb', record)
>>> from collections import namedtuple
>>> Student = namedtuple('Student', 'name serialnum school gradelevel')
>>> Student._make(unpack('<10sHHb', record))
Student(name=b'raymond ', serialnum=4658, school=264, gradelevel=8)
포맷 문자의 순서는 패딩이 암시적이므로 네이티브 모드에서 크기에 영향을 줄 수 있습니다. 표준 모드에서는 사용자가 원하는 패딩을 직접 삽입해야 합니다. 아래 첫 번째 pack 호출에서 패킹된 '#' 뒤에 다음 정수를 4바이트 경계로 정렬하기 위해 세 개의 NUL 바이트가 추가되었습니다. 이 예제는 little-endian 머신에서 생성되었습니다:
>>> pack('@ci', b'#', 0x12131415)
b'#\x00\x00\x00\x15\x14\x13\x12'
>>> pack('@ic', 0x12131415, b'#')
b'\x15\x14\x13\x12#'
>>> calcsize('@ci')
8
>>> calcsize('@ic')
5
다음 포맷 'llh0l'은 플랫폼의 long이 4바이트 경계로 정렬된다고 가정할 때 끝에 두 개의 패딩 바이트가 추가됩니다:
>>> pack('@llh0l', 1, 2, 3)
b'\x00\x00\x00\x01\x00\x00\x00\x02\x00\x03\x00\x00'
See also
- Module array — 동질 데이터의 패킹된 이진 저장.
- Module json — JSON 인코더 및 디코더.
- Module pickle — Python 객체 직렬화.
응용
struct 모듈에는 두 가지 주요 응용이 있습니다: 응용 프로그램 내에서 또는 같은 컴파일러로 컴파일된 다른 응용 프로그램에서 Python과 C 코드 사이의 데이터 교환(네이티브 포맷), 그리고 합의된 데이터 레이아웃을 사용하는 응용 프로그램 사이의 데이터 교환(표준 포맷). 일반적으로 이 두 영역을 위해 구성된 포맷 문자열은 구별됩니다.
네이티브 포맷
네이티브 레이아웃을 모방하는 포맷 문자열을 구성할 때 컴파일러와 머신 아키텍처가 바이트 순서와 패딩을 결정합니다. 이 경우 @ 포맷 문자를 사용하여 네이티브 바이트 순서와 데이터 크기를 지정해야 합니다. 내부 패딩 바이트는 일반적으로 자동으로 삽입됩니다. 연속된 데이터 청크의 올바른 정렬을 위해 포맷 문자열 끝에 0-반복 포맷 코드가 필요할 수 있습니다.
다음 두 가지 간단한 예제를 고려하세요(64비트 little-endian 머신에서):
>>> calcsize('@lhl')
24
>>> calcsize('@llh')
18
두 번째 포맷 문자열의 끝에 데이터가 추가 패딩 없이 8바이트 경계로 패딩되지 않습니다. 0-반복 포맷 코드가 그 문제를 해결합니다:
>>> calcsize('@llh0l')
24
'x' 포맷 코드를 사용하여 반복을 지정할 수 있지만, 네이티브 포맷에서는 '0l'과 같은 0-반복 포맷을 사용하는 것이 더 좋습니다.
기본적으로 네이티브 바이트 순서와 정렬이 사용되지만 명시적으로 '@' 접두 문자를 사용하는 것이 더 좋습니다.
표준 포맷
네트워킹이나 저장과 같이 프로세스 너머로 데이터를 교환할 때는 정확해야 합니다. 정확한 바이트 순서, 크기 및 정렬을 지정하세요. 특정 머신의 네이티브 순서와 일치한다고 가정하지 마세요. 예를 들어 네트워크 바이트 순서는 big-endian이지만 많은 인기 있는 CPU는 little-endian입니다. 이것을 명시적으로 정의하면 사용자는 코드가 실행되는 플랫폼의 세부 사항에 신경 쓸 필요가 없습니다. 첫 번째 문자는 일반적으로 < 또는 >(또는 !)여야 합니다. 패딩은 프로그래머의 책임입니다. 0-반복 포맷 문자는 작동하지 않습니다. 대신 사용자는 필요할 때 'x' 패딩 바이트를 명시적으로 추가해야 합니다. 이전 섹션의 예제를 다시 보면:
>>> calcsize('<qh6xq')
24
>>> pack('<qh6xq', 1, 2, 3) == pack('@lhl', 1, 2, 3)
True
>>> calcsize('@llh')
18
>>> pack('@llh', 1, 2, 3) == pack('<qqh', 1, 2, 3)
True
>>> calcsize('<qqh6x')
24
>>> calcsize('@llh0l')
24
>>> pack('@llh0l', 1, 2, 3) == pack('<qqh6x', 1, 2, 3)
True
위 결과(64비트 머신에서 실행)는 다른 머신에서 실행하면 일치한다는 보장이 없습니다. 예를 들어 아래 예제는 32비트 머신에서 실행되었습니다:
>>> calcsize('<qqh6x')
24
>>> calcsize('@llh0l')
12
>>> pack('@llh0l', 1, 2, 3) == pack('<qqh6x', 1, 2, 3)
False
클래스
struct 모듈은 다음 타입도 정의합니다.
struct.Struct(format)
포맷 문자열 format에 따라 이진 데이터를 쓰고 읽는 새 Struct 객체를 반환합니다. Struct 객체를 한 번 생성하고 그 메서드를 호출하는 것은 같은 포맷으로 모듈 수준 함수를 호출하는 것보다 더 효율적입니다. 포맷 문자열이 한 번만 컴파일되기 때문입니다.
Note
모듈 수준 함수에 전달된 가장 최근 포맷 문자열의 컴파일된 버전은 캐시되므로, 몇 개의 포맷 문자열만 사용하는 프로그램은 단일
Struct인스턴스를 재사용하는 것에 대해 걱정할 필요가 없습니다.
컴파일된 Struct 객체는 다음 메서드와 속성을 지원합니다.
- pack(v1, v2, ...) — 컴파일된 포맷을 사용하여
pack()함수와 동일합니다. (len(result)는size와 같습니다.) - pack_into(buffer, offset, v1, v2, ...) — 컴파일된 포맷을 사용하여
pack_into()함수와 동일합니다. - unpack(buffer) — 컴파일된 포맷을 사용하여
unpack()함수와 동일합니다. 버퍼의 바이트 크기는size와 같아야 합니다. - unpack_from(buffer, offset=0) — 컴파일된 포맷을 사용하여
unpack_from()함수와 동일합니다. 위치offset에서 시작하는 버퍼의 바이트 크기는 최소size여야 합니다. - iter_unpack(buffer) — 컴파일된 포맷을 사용하여
iter_unpack()함수와 동일합니다. 버퍼의 바이트 크기는size의 배수여야 합니다.versionadded: 3.4.
- format — 이
Struct객체를 구성하는 데 사용된 포맷 문자열.versionchanged: 3.7에서 포맷 문자열 타입은 이제 bytes가 아닌 str입니다.
- size —
format에 해당하는 struct의 계산된 크기(따라서pack()메서드가 생성하는 bytes 객체의 크기).versionchanged: 3.13에서 struct의
repr()이 변경되었습니다. 이제 다음과 같이 표시됩니다:>>> Struct('i') Struct('i')