Python의 struct 모듈로 bytes 객체와 C 구조체 표현 사이를 변환하는 방법, 형식 문자열, 바이트 순서, 크기, 정렬, 함수, 클래스, 예제를 설명합니다.
소스 코드:Lib/struct.py
이 모듈은 Python bytes 객체로 표현된 C 구조체와 Python 값 사이를 변환합니다. 간결한 형식 문자열은 Python 값으로/에서 수행할 변환을 설명합니다. 이 모듈의 함수와 객체는 크게 두 가지 서로 구별되는 용도로 사용할 수 있습니다. 외부 소스(파일 또는 네트워크 연결)와의 데이터 교환, 또는 Python 애플리케이션과 C 계층 사이의 데이터 전송입니다.
참고
접두 문자가 주어지지 않으면 기본값은 네이티브 모드입니다. 이 모드는 Python 인터프리터가 빌드된 플랫폼과 컴파일러를 기준으로 데이터를 패킹하거나 언패킹합니다. 주어진 C 구조체를 패킹한 결과에는 관련된 C 형식의 올바른 정렬을 유지하기 위한 패드 바이트가 포함되며, 마찬가지로 언패킹할 때도 정렬이 고려됩니다. 반대로 외부 소스와 데이터를 주고받을 때는 요소 사이의 바이트 순서와 패딩을 프로그래머가 직접 정의해야 합니다. 자세한 내용은 바이트 순서, 크기, 정렬을 참조하세요.
여러 struct 함수(그리고 Struct의 메서드)는 buffer 인수를 받습니다. 이는 버퍼 프로토콜을 구현하고 읽기 가능하거나 읽기-쓰기 가능한 버퍼를 제공하는 객체를 가리킵니다. 이 목적에 가장 흔히 사용되는 형식은 bytes와 bytearray이지만, 바이트 배열로 볼 수 있는 다른 많은 형식도 버퍼 프로토콜을 구현하므로 bytes 객체로부터 추가 복사 없이 읽거나 채울 수 있습니다.
이 모듈은 다음 예외와 함수를 정의합니다:
exception 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 은 필수 인수라는 점에 유의하세요. 음수 offset 은 buffer 의 끝에서부터 계산합니다.
struct.unpack(format, buffer)¶
형식 문자열 format 에 따라 버퍼 buffer (아마도 pack(format, ...) 으로 패킹된 것)에서 언패킹합니다. 결과는 항목이 정확히 하나뿐이어도 튜플입니다. 버퍼의 바이트 단위 크기는 형식이 요구하는 크기와 일치해야 하며, 이는 calcsize()에 반영됩니다.
struct.unpack_from(format, /, buffer, offset=0)¶
형식 문자열 format 에 따라 위치 offset 에서 시작해 buffer 에서 언패킹합니다. 결과는 항목이 정확히 하나뿐이어도 튜플입니다. 위치 offset 부터 시작한 버퍼의 바이트 단위 크기는 형식이 요구하는 크기 이상이어야 하며, 이는 calcsize()에 반영됩니다. 음수 offset 은 buffer 의 끝에서부터 계산합니다.
struct.iter_unpack(format, buffer)¶
형식 문자열 format 에 따라 버퍼 buffer 에서 반복적으로 언패킹합니다. 이 함수는 버퍼의 모든 내용이 소모될 때까지 동일한 크기의 조각을 읽는 이터레이터를 반환합니다. 버퍼의 바이트 단위 크기는 형식이 요구하는 크기의 배수여야 하며, 이는 calcsize()에 반영됩니다.
각 반복은 형식 문자열에 지정된 대로 튜플을 산출합니다.
버전 3.4에서 추가됨.
struct.calcsize(format)¶
형식 문자열 format 에 대응하는 구조체의 크기(따라서 pack(format, ...) 이 생성하는 bytes 객체의 크기)를 반환합니다.
형식 문자열은 데이터를 패킹하고 언패킹할 때의 데이터 배치를 설명합니다. 형식 문자열은 패킹/언패킹되는 데이터의 형식을 지정하는 형식 문자로 구성됩니다. 또한 특수 문자는 바이트 순서, 크기, 정렬을 제어합니다. 각 형식 문자열은 데이터의 전체 속성을 설명하는 선택적 접두 문자 하나와 실제 데이터 값 및 패딩을 설명하는 하나 이상의 형식 문자로 이루어집니다.
기본적으로 C 형식은 시스템의 네이티브 형식과 바이트 순서로 표현되며, 필요하면 패드 바이트를 건너뛰어 올바르게 정렬됩니다(C 컴파일러가 사용하는 규칙에 따름). 이러한 동작은 패킹된 구조체의 바이트가 대응하는 C 구조체의 메모리 배치와 정확히 일치하도록 선택된 것입니다. 네이티브 바이트 순서와 패딩을 사용할지, 표준 형식을 사용할지는 애플리케이션에 따라 다릅니다.
또는 형식 문자열의 첫 번째 문자를 사용하여, 다음 표에 따라 패킹된 데이터의 바이트 순서, 크기, 정렬을 지정할 수 있습니다:
| 문자 | 바이트 순서 | 크기 | 정렬 |
|---|---|---|---|
@ | 네이티브 | 네이티브 | 네이티브 |
= | 네이티브 | 표준 | 없음 |
< | 리틀 엔디언 | 표준 | 없음 |
> | 빅 엔디언 | 표준 | 없음 |
! | 네트워크 (= 빅 엔디언) | 표준 | 없음 |
첫 번째 문자가 이들 중 하나가 아니면 '@' 가 가정됩니다.
참고
숫자 1023(16진수로 0x3ff)는 다음과 같은 바이트 표현을 가집니다:
빅 엔디언(>)에서는 03 ff
리틀 엔디언(<)에서는 ff 03
Python 예제:
import struct struct.pack('>h', 1023) b'\x03\xff' struct.pack('<h', 1023) b'\xff\x03'
네이티브 바이트 순서는 호스트 시스템에 따라 빅 엔디언이거나 리틀 엔디언입니다. 예를 들어 Intel x86, AMD64 (x86-64), Apple M1은 리틀 엔디언이고, IBM z와 많은 레거시 아키텍처는 빅 엔디언입니다. 시스템의 엔디언 방식을 확인하려면 sys.byteorder를 사용하세요.
네이티브 크기와 정렬은 C 컴파일러의 sizeof 식을 사용해 결정됩니다. 이것은 항상 네이티브 바이트 순서와 함께 사용됩니다.
표준 크기는 형식 문자에만 의존합니다. 형식 문자 절의 표를 참조하세요.
'@' 와 '=' 의 차이에 유의하세요. 둘 다 네이티브 바이트 순서를 사용하지만, 후자의 크기와 정렬은 표준화되어 있습니다.
형식 '!' 는 IETF RFC 1700에 정의된 네트워크 바이트 순서를 나타내며, 이는 항상 빅 엔디언입니다.
비네이티브 바이트 순서를 표시하는 방법(강제로 바이트 스와핑)은 없습니다. 적절하게 '<' 또는 '>' 를 선택해 사용하세요.
참고 사항:
패딩은 연속된 구조체 멤버 사이에만 자동으로 추가됩니다. 인코딩된 구조체의 시작이나 끝에는 패딩이 추가되지 않습니다.
비네이티브 크기와 정렬, 예를 들어 ‘<’, ‘>’, ‘=’, ‘!’를 사용할 때는 패딩이 추가되지 않습니다.
구조체의 끝을 특정 형식의 정렬 요구 사항에 맞추려면, 반복 횟수가 0인 그 형식의 코드를 형식 끝에 추가하세요. 예제를 참조하세요.
형식 문자의 의미는 다음과 같습니다. C 값과 Python 값 사이의 변환은 형식만 보면 분명합니다. ‘표준 크기’ 열은 표준 크기를 사용할 때, 즉 형식 문자열이 '<', '>', '!', '=' 중 하나로 시작할 때 패킹된 값의 바이트 단위 크기를 나타냅니다. 네이티브 크기를 사용할 때는 패킹된 값의 크기가 플랫폼에 따라 달라집니다.
| 형식 | C 형식 | Python 형식 | 표준 크기 | 참고 |
|---|---|---|---|---|
x | 패드 바이트 | 값 없음 | (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) |
버전 3.3에서 변경: 'n' 및 'N' 형식 지원 추가.
버전 3.6에서 변경: 'e' 형식 지원 추가.
버전 3.14에서 변경: 'F' 및 'D' 형식 지원 추가.
참고
array 및 ctypes 모듈, 그리고 numpy 같은 서드파티 모듈은 유사하지만 약간 다른 형식 코드를 사용합니다.
참고 사항:
'?' 변환 코드는 C99 이후 C 표준에서 정의된 _Bool 형식에 대응합니다. 표준 모드에서는 1바이트로 표현됩니다.
정수 변환 코드 중 하나를 사용해 정수가 아닌 값을 패킹하려 할 때, 그 값이 __index__() 메서드를 가지고 있으면 패킹 전에 그 메서드를 호출하여 인수를 정수로 변환합니다.
버전 3.2에서 변경: 정수가 아닌 값에 대해 __index__() 메서드 사용 추가.
'n' 및 'N' 변환 코드는 네이티브 크기(기본값이거나 '@' 바이트 순서 문자를 선택한 경우)에서만 사용할 수 있습니다. 표준 크기에서는 애플리케이션에 맞는 다른 정수 형식 중 하나를 사용하면 됩니다.
'f', 'd', 'e' 변환 코드의 경우, 패킹된 표현은 플랫폼이 사용하는 부동소수점 형식과 관계없이 IEEE 754 binary32, binary64, binary16 형식(각각 'f', 'd', 'e')을 사용합니다.
'P' 형식 문자는 네이티브 바이트 순서(기본값이거나 '@' 바이트 순서 문자를 선택한 경우)에서만 사용할 수 있습니다. 바이트 순서 문자 '=' 는 호스트 시스템에 따라 리틀 엔디언 또는 빅 엔디언 순서를 선택합니다. struct 모듈은 이를 네이티브 순서로 해석하지 않으므로 'P' 형식은 사용할 수 없습니다.
IEEE 754 binary16 “half precision” 형식은 IEEE 754 표준의 2008년 개정판에서 도입되었습니다. 이 형식은 부호 비트, 5비트 지수, 11비트 정밀도(그중 10비트는 명시적으로 저장됨)를 가지며, 완전한 정밀도에서 대략 6.1e-05 와 6.5e+04 사이의 수를 표현할 수 있습니다. 이 형식은 C 컴파일러에서 널리 지원되지 않으며, 컴파일러가 C23 표준의 Annex H를 지원하는 경우 _Float16 형식으로 사용할 수 있습니다. 일반적인 시스템에서는 저장용으로 unsigned short를 사용할 수 있지만, 수학 연산용으로는 사용할 수 없습니다. 자세한 내용은 half-precision floating-point format에 대한 Wikipedia 문서를 참조하세요.
패킹할 때 'x' 는 NUL 바이트 하나를 삽입합니다.
'p' 형식 문자는 “Pascal 문자열”을 인코딩합니다. 이는 개수로 지정되는 고정된 바이트 수 안에 저장되는 짧은 가변 길이 문자열을 뜻합니다. 저장되는 첫 번째 바이트는 문자열의 길이 또는 255 중 더 작은 값입니다. 그 뒤에 문자열의 바이트가 따라옵니다. pack()에 전달된 바이트 문자열이 너무 길면(개수에서 1을 뺀 값보다 길면) 문자열의 앞쪽 count-1 바이트만 저장됩니다. 바이트 문자열이 count-1 보다 짧으면 전체 사용 바이트 수가 정확히 count가 되도록 널 바이트로 패딩됩니다. unpack()의 경우 'p' 형식 문자는 count 바이트를 소비하지만, 반환되는 bytes 객체는 255바이트를 초과할 수 없다는 점에 유의하세요. 패킹할 때는 bytes 및 bytearray 형식의 인수를 허용합니다.
's' 형식 문자의 경우 개수는 다른 형식 문자처럼 반복 횟수가 아니라 바이트 문자열의 길이로 해석됩니다. 예를 들어 '10s' 는 단일 Python 바이트 문자열 하나로/에서 매핑되는 하나의 10바이트 문자열을 의미하는 반면, '10c' 는 서로 다른 열 개의 Python 바이트 객체로/에서 매핑되는 10개의 개별 1바이트 문자 요소(예: cccccccccc)를 의미합니다. (차이를 구체적으로 보여주는 예시는 예제를 참조하세요.) 개수가 주어지지 않으면 기본값은 1입니다. 패킹할 때는 맞도록 바이트 문자열을 잘라내거나 널 바이트로 패딩합니다. 언패킹할 때 결과 bytes 객체는 항상 정확히 지정된 바이트 수를 가집니다. 특별한 경우로 '0s' 는 비어 있는 단일 바이트 문자열을 의미하고('0c' 는 문자 0개를 의미함), 패킹할 때는 bytes 및 bytearray 형식의 인수를 허용합니다.
'F' 및 'D' 형식 문자의 경우, 패킹된 표현은 플랫폼이 사용하는 부동소수점 형식과 관계없이 복소수의 각 성분에 대해 IEEE 754 binary32 및 binary64 형식을 사용합니다. 복소수 형식(F 및 D)은 C에서 복소수 형식이 선택적 기능임에도 불구하고 조건 없이 사용할 수 있다는 점에 유의하세요. C11 표준에 명시된 대로, 각 복소수 형식은 각각 실수부와 허수부를 담는 2원소 C 배열로 표현됩니다.
형식 문자 앞에는 정수 반복 횟수를 붙일 수 있습니다. 예를 들어 형식 문자열 '4h' 는 'hhhh' 와 정확히 같은 의미입니다.
형식 사이의 공백 문자는 무시됩니다. 하지만 개수와 그 형식 사이에는 공백이 있으면 안 됩니다.
정수 형식('b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q') 중 하나를 사용해 값 x 를 패킹할 때, x 가 해당 형식의 유효 범위를 벗어나면 struct.error가 발생합니다.
버전 3.1에서 변경: 이전에는 일부 정수 형식이 범위를 벗어난 값을 순환 처리하고 struct.error 대신 DeprecationWarning을 발생시켰습니다.
'?' 형식 문자의 경우 반환값은 True 또는 False입니다. 패킹할 때는 인수 객체의 진릿값이 사용됩니다. 네이티브 또는 표준 bool 표현에서 0 또는 1이 패킹되며, 언패킹할 때 0이 아닌 값은 모두 True 가 됩니다.
참고
네이티브 바이트 순서 예제('@' 형식 접두사를 사용하거나 접두 문자가 전혀 없는 경우)는 플랫폼과 컴파일러에 따라 달라지므로 독자의 시스템에서 생성되는 결과와 일치하지 않을 수 있습니다.
빅 엔디언 순서를 사용하여 세 가지 서로 다른 크기의 정수를 패킹하고 언패킹합니다:
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 바이트 세 개가 추가되었음을 주목하세요. 이 예제의 출력은 리틀 엔디언 시스템에서 생성되었습니다:
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'
참고
모듈 array
동종 데이터의 패킹된 이진 저장.
모듈 json
JSON 인코더와 디코더.
모듈 pickle
Python 객체 직렬화.
struct 모듈의 주요 응용은 두 가지입니다. 하나는 애플리케이션 내부 또는 같은 컴파일러로 컴파일된 다른 애플리케이션의 Python과 C 코드 사이의 데이터 교환(네이티브 형식)이고, 다른 하나는 합의된 데이터 배치를 사용하는 애플리케이션 사이의 데이터 교환(표준 형식)입니다. 일반적으로 이 두 영역을 위해 구성되는 형식 문자열은 서로 구별됩니다.
네이티브 배치를 모방하는 형식 문자열을 구성할 때는 컴파일러와 시스템 아키텍처가 바이트 순서와 패딩을 결정합니다. 이런 경우에는 네이티브 바이트 순서와 데이터 크기를 지정하기 위해 @ 형식 문자를 사용해야 합니다. 내부 패드 바이트는 보통 자동으로 삽입됩니다. 연속된 데이터 덩어리를 올바르게 정렬하기 위한 정확한 바이트 경계로 반올림하려면 형식 문자열 끝에 반복 횟수가 0인 형식 코드가 필요할 수 있습니다.
다음 두 단순한 예를 살펴보세요(64비트 리틀 엔디언 시스템에서):
calcsize('@lhl') 24 calcsize('@llh') 18
두 번째 형식 문자열의 끝은 추가 패딩을 사용하지 않으면 8바이트 경계로 패딩되지 않습니다. 반복 횟수가 0인 형식 코드가 이 문제를 해결합니다:
calcsize('@llh0l') 24
'x' 형식 코드를 사용해 반복을 지정할 수 있지만, 네이티브 형식에서는 '0l' 같은 반복 0 형식을 사용하는 것이 더 좋습니다.
기본적으로 네이티브 바이트 순서와 정렬이 사용되지만, '@' 접두 문자를 사용해 명시적으로 지정하는 편이 더 좋습니다.
네트워킹이나 저장소처럼 프로세스 밖으로 데이터를 주고받을 때는 정확해야 합니다. 정확한 바이트 순서, 크기, 정렬을 지정하세요. 그것들이 특정 시스템의 네이티브 순서와 같다고 가정하지 마세요. 예를 들어 네트워크 바이트 순서는 빅 엔디언이지만, 많은 대중적인 CPU는 리틀 엔디언입니다. 이를 명시적으로 정의하면 사용자는 자신의 코드가 실행되는 플랫폼의 세부 사항을 신경 쓸 필요가 없습니다. 첫 번째 문자는 보통 < 또는 > (또는 !) 이어야 합니다. 패딩은 프로그래머의 책임입니다. 반복 횟수가 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 모듈은 다음 형식도 정의합니다:
class struct.Struct(format)¶
형식 문자열 format 에 따라 이진 데이터를 쓰고 읽는 새 Struct 객체를 반환합니다. Struct 객체를 한 번 생성하고 그 메서드를 호출하는 것은, 같은 형식으로 모듈 수준 함수를 호출하는 것보다 효율적입니다. 형식 문자열이 한 번만 컴파일되기 때문입니다.
참고
모듈 수준 함수에 전달된 가장 최근 형식 문자열의 컴파일된 버전은 캐시되므로, 몇 개 안 되는 형식 문자열만 사용하는 프로그램은 하나의 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 의 배수여야 합니다.
버전 3.4에서 추가됨.
format¶ 이 Struct 객체를 생성하는 데 사용된 형식 문자열입니다.
버전 3.7에서 변경: 형식 문자열의 형식이 이제 bytes 대신 str 입니다.
size¶
format 에 대응하는 구조체의 계산된 크기(따라서 pack() 메서드가 생성하는 bytes 객체의 크기)입니다.
버전 3.13에서 변경: struct의 repr() 이 변경되었습니다. 이제 다음과 같습니다:
Struct('i') Struct('i')