"struct" --- 将字节串解读为打包的二进制数据
*******************************************

**源代码：** Lib/struct.py

======================================================================

此模块可在 Python 值和以 Python "bytes" 对象表示的 C 结构体之间进行转
换。通过紧凑 格式字符串 描述预期的 Python 值转换目标/来源。 此模块的函
数和对象可被用于两种相当不同的应用程序，与外部源（文件或网络连接）进行
数据交换，或者在 Python 应用和 C 层级之间进行数据传输。

备注:

  当未给出前缀字符时，将默认为原生模式。它会基于构建 Python 解释器的平
  台和编译器来打包和解包数据。打包一个给定 C 结构体的结果包括为所涉及
  的 C 类型保持正确对齐的填充字节；类似地，当解包时也会将对齐纳入考虑
  。 相反地，当在外部源之间进行数据通信时，将由程序员负责定义字节顺序
  和元素之间的填充。请参阅 字节顺序，大小和对齐方式 了解详情。

某些 "struct" 函数（和 "Struct" 的方法）接受一个 *buffer* 参数。这是指
实现了 缓冲协议 并提供可读或可读写缓冲区的对象。用于此目的的最常见类型
是 "bytes" 和 "bytearray"，但许多其他可被视为字节数组的类型也实现了缓
冲区协议，因此它们可以在无需从 "bytes" 对象进行额外复制的情况下被读取/
填充。


函数和异常
==========

此模块定义了下列异常和函数：

exception struct.error

   会在多种场合下被引发的异常；其参数为一个描述错误信息的字符串。

struct.pack(format, v1, v2, ...)

   返回一个 bytes 对象，其中包含根据格式字符串 *format* 打包的值 *v1*,
   *v2*, ... 参数个数必须与格式字符串所要求的值完全匹配。

struct.pack_into(format, buffer, offset, v1, v2, ...)

   根据格式字符串 *format* 打包 *v1*, *v2*, ... 等值并将打包的字节串写
   入可写缓冲区 *buffer* 从 *offset* 开始的位置。请注意 *offset* 是必
   需的参数。

struct.unpack(format, buffer)

   根据格式字符串 *format* 从缓冲区 *buffer* 解包（假定是由
   "pack(format, ...)" 打包）。 结果为一个元组，即使其只包含一个条目。
   缓冲区的字节大小必须匹配格式所要求的大小，如 "calcsize()" 所示。

struct.unpack_from(format, /, buffer, offset=0)

   对 *buffer* 从位置 *offset* 开始根据格式字符串 *format* 进行解包。
   结果为一个元组，即使其中只包含一个条目。 缓冲区的字节大小从位置
   *offset* 开始必须至少为 "calcsize()" 显示的格式所要求的大小。

struct.iter_unpack(format, buffer)

   根据格式字符串 *format* 以迭代方式从缓冲区 *buffer* 中解包。 此函数
   返回一个迭代器，它将从缓冲区读取大小相等的块直到其所有内容耗尽为止
   。缓冲区的字节大小必须是格式所要求的大小的整数倍，如 "calcsize()"
   所显示的。

   每次迭代将产生一个如格式字符串所指定的元组。

   Added in version 3.4.

struct.calcsize(format)

   返回与格式字符串 *format* 相对应的结构的大小（亦即 "pack(format,
   ...)" 所产生的字节串对象的大小）。


格式字符串
==========

Format strings describe the data layout when packing and unpacking
data.  They are built up from type codes, which specify the type of
data being packed/unpacked.  In addition, special characters control
the byte order, size and alignment. Each format string consists of an
optional prefix character which describes the overall properties of
the data and one or more format characters which describe the actual
data values and padding.


字节顺序，大小和对齐方式
------------------------

在默认情况下，C 类型将以所在机器的原生格式和字节顺序来表示，并在必要时
通过跳过填充字节来正确地对齐（根据 C 编译器所使用的规则）。 选择此行为
是为了使已打包结构体的字节与对应的 C 结构体的内存布局完全对应。使用原
生字节顺序和填充还是标准格式取决于应用程序本身。

或者，根据下表，格式字符串的第一个字符可用于指示打包数据的字节顺序，大
小和对齐方式：

+-------------+--------------------------+------------+-------------+
| 字符        | 字节顺序                 | 大小       | 对齐方式    |
|=============|==========================|============|=============|
| "@"         | 原生                     | 原生       | 原生        |
+-------------+--------------------------+------------+-------------+
| "="         | 原生                     | 标准       | 无          |
+-------------+--------------------------+------------+-------------+
| "<"         | 小端                     | 标准       | 无          |
+-------------+--------------------------+------------+-------------+
| ">"         | 大端                     | 标准       | 无          |
+-------------+--------------------------+------------+-------------+
| "!"         | 网络（=大端）            | 标准       | 无          |
+-------------+--------------------------+------------+-------------+

如果第一个字符不是其中之一，则假定为 "'@'"。

备注:

  数字 1023 (十六进制的 "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" 表达式来确定的。这总是会
与本机字节顺序相绑定。

Standard size depends only on the type code;  see the table in the
Type Codes section.

请注意 "'@'" 和 "'='" 之间的区别：两个都使用本机字节顺序，但后者的大小
和对齐方式是标准化的。

形式 "'!'" 代表网络字节顺序总是使用在 IETF RFC 1700 中所定义的大端序。

没有什么方式能指定非本机字节顺序（强制字节对调）；请正确选择使用 "'<'"
或 "'>'"。

注意：

1. 填充只会在连续结构成员之间自动添加。填充不会添加到已编码结构的开头
   和末尾。

2. 当使用非本机大小和对齐方式即 '<', '>', '=', and '!' 时不会添加任何
   填充。

3. 要将结构的末尾对齐到符合特定类型的对齐要求，请以该类型代码加重复计
   数的零作为格式结束。参见 例子。


Type Codes
----------

Type codes (or format codes) have the following meaning; the
conversion between C and Python values should be obvious given their
types.  The 'Standard size' column refers to the size of the packed
value in bytes when using standard size; that is, when the format
string starts with one of "'<'", "'>'", "'!'" or "'='".  When using
native size, the size of the packed value is platform-dependent.

+----------+----------------------------+----------------------+------------------+--------------+
| 格式     | C 类型                     | Python 类型          | 标准大小         | 备注         |
|==========|============================|======================|==================|==============|
| "x"      | 填充字节                   | 无                   |                  | (7)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "c"      | char                       | 长度为 1 的字节串    | 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              | 复数                 | 8                | (10)         |
+----------+----------------------------+----------------------+------------------+--------------+
| "D"      | double complex             | 复数                 | 16               | (10)         |
+----------+----------------------------+----------------------+------------------+--------------+
| "Zf"     | float complex              | 复数                 | 8                | (10)         |
+----------+----------------------------+----------------------+------------------+--------------+
| "Zd"     | double complex             | 复数                 | 16               | (10)         |
+----------+----------------------------+----------------------+------------------+--------------+
| "s"      | char[]                     | 字节串               |                  | (9)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "p"      | char[]                     | 字节串               |                  | (8)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "P"      | void*                      | int                  |                  | (2), (5)     |
+----------+----------------------------+----------------------+------------------+--------------+

在 3.3 版本发生变更: 增加了对 "'n'" 和 "'N'" 格式的支持

在 3.6 版本发生变更: 添加了对 "'e'" 格式的支持。

在 3.14 版本发生变更: 增加了对 "'F'" 和 "'D'" 格式的支持。

在 3.15 版本发生变更: Added support for the "'Zf'" and "'Zd'" formats.

参见: "array" 和 ctypes 模块，以及某些第三方模块如 numpy 均使用类似 -- 但
    略有差异的 -- 类型代码。

注意：

1. "'?'" 转换码对应于自 C99 开始由 C 标准所定义的 _Bool 类型。在标准模
   式下，它总是以一个字节来表示。

2. 当尝试使用任何整数转换码打包一个非整数时，如果该非整数具有
   "__index__()" 方法，则会在打包之前将参数转换为一个整数。

   在 3.2 版本发生变更: 增加了用于非整数的 "__index__()" 方法。

3. "'n'" 和 "'N'" 转换码仅对本机大小可用（选择为默认或使用 "'@'" 字节
   顺序字符）。 对于标准大小，你可以使用适合你的应用的任何其他整数格式
   。

4. 对于 "'f'", "'d'" 和 "'e'" 转换码，打包表示形式将使用 IEEE 754
   binary32, binary64 或 binary16 格式 (分别对应于 "'f'", "'d'" 或
   "'e'")，无论平台使用何种浮点格式。

5. The "'P'" type code is only available for the native byte ordering
   (selected as the default or with the "'@'" byte order character).
   The byte order character "'='" chooses to use little- or big-endian
   ordering based on the host system. The struct module does not
   interpret this as native ordering, so the "'P'" format is not
   available.

6. IEEE 754 binary16 "半精度" 类型是在 IEEE 754 标准 的 2008 修订版中
   引入的。 它包含一个符号位，5 个指数位和 11 个精度位（明确存储 10 位
   ），可用完全精度表示大致范围在 "6.1e-05" 和 "6.5e+04" 之间的数字。
   此类型并不被 C 编译器广泛支持：它可以作为 _Float16 类型，如果编译器
   支持 C23 标准附件 H 的话。 在一台典型的机器上，可以使用 unsigned
   short 来存储，但不会被用于数学运算。 请参阅 Wikipedia 页面 half-
   precision floating-point format 了解详情。

7. 在打包时，"'x'" 会插入一个 NUL 字节。

8. The "'p'" type code encodes a "Pascal string", meaning a short
   variable-length string stored in a *fixed number of bytes*, given
   by the count. The first byte stored is the length of the string, or
   255, whichever is smaller.  The bytes of the string follow.  If the
   byte string passed in to "pack()" is too long (longer than the
   count minus 1), only the leading "count-1" bytes of the string are
   stored.  If the byte string is shorter than "count-1", it is padded
   with null bytes so that exactly count bytes in all are used.  Note
   that for "unpack()", the "'p'" type code consumes "count" bytes,
   but that the "bytes" object returned can never contain more than
   255 bytes. When packing, arguments of types "bytes" and "bytearray"
   are accepted.

9. For the "'s'" type code, the count is interpreted as the length of
   the byte string, not a repeat count like for the other type codes;
   for example, "'10s'" means a single 10-byte string mapping to or
   from a single Python byte string, while "'10c'" means 10 separate
   one byte character elements (e.g., "cccccccccc") mapping to or from
   ten different Python byte objects. (See 例子 for a concrete
   demonstration of the difference.) If a count is not given, it
   defaults to 1.  For packing, the byte string is truncated or padded
   with null bytes as appropriate to make it fit. For unpacking, the
   resulting "bytes" object always has exactly the specified number of
   bytes.  As a special case, "'0s'" means a single, empty byte string
   (while "'0c'" means 0 characters). When packing, arguments of types
   "bytes" and "bytearray" are accepted.

10. For the "'F'" and "'D'" type codes, the packed representation uses
    the IEEE 754 binary32 and binary64 format for components of the
    complex number, regardless of the floating-point format used by
    the platform. Note that complex types ("F"/"Zf" and "D"/"Zd") are
    available unconditionally, despite complex types being an optional
    feature in C. As specified in the C11 standard, each complex type
    is represented by a two-element C array containing, respectively,
    the real and imaginary parts.

A type code may be preceded by an integral repeat count.  For example,
the format string "'4h'" means exactly the same as "'hhhh'".

格式之间的空白字符会被忽略；但是计数及其格式字符中不可有空白字符。

当使用某一种整数格式 ("'b'", "'B'", "'h'", "'H'", "'i'", "'I'", "'l'",
"'L'", "'q'", "'Q'") 打包值 "x" 时，如果 "x" 在该格式的有效范围之外则
将引发 "struct.error".

在 3.1 版本发生变更: 在之前版本中，某些整数格式包装了超范围的值并会引
发 "DeprecationWarning" 而不是 "struct.error"。

For the "'?'" type code, the return value is either "True" or "False".
When packing, the truth value of the argument object is used. Either 0
or 1 in the native or standard bool representation will be packed, and
any non-zero value will be "True" when unpacking.


例子
----

备注:

  原生字节顺序的示例 (由 "'@'" 格式前缀或不带任何前缀字符的形式指定)
  可能与读者机器所产生的内容不匹配，因为这取决于具体的平台和编译器。

打包和解包三种不同大小的整数，使用大端序:

   >>> 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'

解包的字段可通过将它们赋值给变量或将结果包装为一个具名元组来命名:

   >>> 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)

The ordering of type codes may have an impact on size in native mode
since padding is implicit. In standard mode, the user is responsible
for inserting any desired padding. Note in the first "pack" call below
that three NUL bytes were added after the packed "'#'" to align the
following integer on a four-byte boundary. In this example, the output
was produced on a little endian machine:

   >>> 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 代码之间或与使
用同一编译器编译的另一个应用程序之间的数据交换 (原生格式)，以及使用商
定的数据布局在应用程序之间的数据交换 (标准格式)。一般来说，为这两个领
域构造的格式字符串是不同的。


原生格式
--------

When constructing format strings which mimic native layouts, the
compiler and machine architecture determine byte ordering and padding.
In such cases, the "@" format character should be used to specify
native byte ordering and data sizes.  Internal pad bytes are normally
inserted automatically.  It is possible that a zero-repeat type code
will be needed at the end of a format string to round up to the
correct byte boundary for proper alignment of consecutive chunks of
data.

请看这两个简单的示例（在 64 位的小端序机器上）:

   >>> calcsize('@lhl')
   24
   >>> calcsize('@llh')
   18

在不使用额外填充的情况下不会将数据填充到第二个格式字符串末尾的 8 字节
边界上。零重复的格式代码解决了这个问题:

   >>> calcsize('@llh0l')
   24

The "'x'" type code can be used to specify the repeat, but for native
formats it is better to use a zero-repeat format like "'0l'".

在默认情况下，将使用原生字节顺序和对齐，但最好是显式指定并使用 "'@'"
前缀字符。


标准格式
--------

当与你的进程之外如网络或存储交换数据时，请务必保持精确。准确地指定字节
顺序、大小和对齐。不要假定它们与特定机器的原生顺序相匹配。 例如，网络
字节顺序是大端序的，而许多流行的 CPU 则是小端序的。通过显式定义，用户
将无需关心他们的代码运行所在平台的具体规格。第一个字符通常应为 "<" 或
">" (或者 "!")。程序员要负责填充操作。零重复格式字符是无效的。相反，用
户必须在需要时显式地添加 "'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)

   返回一个新的 Struct 对象，它会根据格式字符串 *format* 来写入和读取
   二进制数据。一次性地创建 "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" 的整数倍。

      Added in version 3.4.

   format

      用于构造此 Struct 对象的格式字符串。

      在 3.7 版本发生变更: 格式字符串类型现在是 "str" 而不再是 "bytes"
      。

   size

      计算出对应于 "format" 的结构大小（亦即 "pack()" 方法所产生的字节
      串对象的大小）。

   在 3.13 版本发生变更: 结构体的 *repr()* 已被修改。现在将为：

   >>> Struct('i')
   Struct('i')
