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

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

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

此模块可以执行 Python 值和以 Python "bytes" 对象表示的 C 结构之间的转
换。 这可以被用来处理存储在文件中或是从网络连接等其他来源获取的二进制
数据。 它使用 格式字符串 作为 C 结构布局的精简描述以及与 Python 值的双
向转换。

注解:

  默认情况下，打包给定 C 结构的结果会包含填充字节以使得所涉及的 C 类型
  保持正确的对齐；类似地，对齐在解包时也会被纳入考虑。 选择此种行为的
  目的是使得被打包结构的字节能与相应 C 结构在内存中的布局完全一致。 要
  处理平台独立的数据格式或省略隐式的填充字节，请使用 "standard" 大小和
  对齐而不是 "native" 大小和对齐：详情参见 字节顺序，大小和对齐方式。

某些 "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()" 所示。

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

   3.4 新版功能.

struct.calcsize(format)

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


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

格式字符串是用来在打包和解包数据时指定预期布局的机制。 它们使用指定被
打包/解包数据类型的 格式字符 进行构建。 此外，还有一些特殊字符用来控制
字节顺序，大小和对齐方式。


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

默认情况下，C类型以机器的本机格式和字节顺序表示，并在必要时通过跳过填
充字节进行正确对齐（根据C编译器使用的规则）。

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

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

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

本机字节顺序可能为大端或是小端，取决于主机系统的不同。 例如， Intel
x86 和 AMD64 (x86-64) 是小端的；Motorola 68000 和 PowerPC G5 是大端的
；ARM 和 Intel Itanium 具有可切换的字节顺序（双端）。 请使用
"sys.byteorder" 来检查你的系统字节顺序。

本机大小和对齐方式是使用 C 编译器的 "sizeof" 表达式来确定的。 这总是会
与本机字节顺序相绑定。

标准大小仅取决于格式字符；请参阅 格式字符 部分中的表格。

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

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

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

注释：

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

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

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


格式字符
--------

格式字符具有以下含义；C 和 Python 值之间的按其指定类型的转换应当是相当
明显的。 ‘标准大小’列是指当使用标准大小时以字节表示的已打包值大小；也
就是当格式字符串以 "'<'", "'>'", "'!'" 或 "'='" 之一开头的情况。 当使
用本机大小时，已打包值的大小取决于具体的平台。

+----------+----------------------------+----------------------+------------------+--------------+
| 格式     | C 类型                     | Python 类型          | 标准大小         | 备注         |
|==========|============================|======================|==================|==============|
| "x"      | 填充字节                   | 无                   |                  |              |
+----------+----------------------------+----------------------+------------------+--------------+
| "c"      | "char"                     | 长度为 1 的字节串    | 1                |              |
+----------+----------------------------+----------------------+------------------+--------------+
| "b"      | "signed char"              | integer              | 1                | (1), (2)     |
+----------+----------------------------+----------------------+------------------+--------------+
| "B"      | "unsigned char"            | integer              | 1                | (2)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "?"      | "_Bool"                    | bool                 | 1                | (1)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "h"      | "short"                    | integer              | 2                | (2)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "H"      | "unsigned short"           | integer              | 2                | (2)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "i"      | "int"                      | integer              | 4                | (2)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "I"      | "unsigned int"             | integer              | 4                | (2)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "l"      | "long"                     | integer              | 4                | (2)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "L"      | "unsigned long"            | integer              | 4                | (2)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "q"      | "long long"                | integer              | 8                | (2)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "Q"      | "unsigned long long"       | integer              | 8                | (2)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "n"      | "ssize_t"                  | integer              |                  | (3)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "N"      | "size_t"                   | integer              |                  | (3)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "e"      | (6)                        | float                | 2                | (4)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "f"      | "float"                    | float                | 4                | (4)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "d"      | "double"                   | float                | 8                | (4)          |
+----------+----------------------------+----------------------+------------------+--------------+
| "s"      | "char[]"                   | 字节串               |                  |              |
+----------+----------------------------+----------------------+------------------+--------------+
| "p"      | "char[]"                   | 字节串               |                  |              |
+----------+----------------------------+----------------------+------------------+--------------+
| "P"      | "void *"                   | integer              |                  | (5)          |
+----------+----------------------------+----------------------+------------------+--------------+

在 3.3 版更改: 增加了对 "'n'" 和 "'N'" 格式的支持

在 3.6 版更改: 添加了对 "'e'" 格式的支持。

注释：

1. "'?'" 转换码对应于 C99 定义的 "_Bool" 类型。 如果此类型不可用，则使
   用 "char" 来模拟。 在标准模式下，它总是以一个字节表示。

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

   在 3.2 版更改: 增加了针对非整数使用 "__index__()" 方法的特性。

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

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

5. "'P'" 格式字符仅对本机字节顺序可用（选择为默认或使用 "'@'" 字节顺序
   字符）。 字节顺序字符 "'='" 选择使用基于主机系统的小端或大端排序。
   struct 模块不会将其解读为本机排序，因此 "'P'" 格式将不可用。

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

格式字符之前可以带有整数重复计数。 例如，格式字符串 "'4h'" 的含义与
"'hhhh'" 完全相同。

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

对于 "'s'" 格式字符，计数会被解析为字节的长度，而不是像其他格式字符那
样的重复计数；例如，"'10s'" 表示一个 10 字节的字节串，而 "'10c'" 表示
10 个字符。 如果未给出计数，则默认值为 1。 对于打包操作，字节串会被适
当地截断或填充空字节以符合要求。 对于解包操作，结果字节对象总是恰好具
有指定数量的字节。 作为特殊情况，"'0s'" 表示一个空字符串（而 "'0c'" 表
示 0 个字符）。

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

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

"'p'" 格式字符用于编码“Pascal 字符串”，即存储在由计数指定的 *固定长度
字节* 中的可变长度短字符串。 所存储的第一个字节为字符串长度或 255 中的
较小值。 之后是字符串对应的字节。 如果传入 "pack()" 的字符串过长（超过
计数值减 1），则只有字符串前 "count-1" 个字节会被存储。 如果字符串短于
"count-1"，则会填充空字节以使得恰好使用了 count 个字节。 请注意对于
"unpack()"，"'p'" 格式字符会消耗 "count" 个字节，但返回的字符串永远不
会包含超过 255 个字节。

对于 "'?'" 格式字符，返回值为 "True" 或 "False"。 在打包时将会使用参数
对象的逻辑值。 以本机或标准 bool 类型表示的 0 或 1 将被打包，任何非零
值在解包时将为 "True"。


例子
----

注解:

  所有示例都假定使用一台大端机器的本机字节顺序、大小和对齐方式。

打包/解包三个整数的基础示例:

   >>> from struct import *
   >>> pack('hhl', 1, 2, 3)
   b'\x00\x01\x00\x02\x00\x00\x00\x03'
   >>> unpack('hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')
   (1, 2, 3)
   >>> calcsize('hhl')
   8

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

   >>> 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('ci', b'*', 0x12131415)
   b'*\x00\x00\x00\x12\x13\x14\x15'
   >>> pack('ic', 0x12131415, b'*')
   b'\x12\x13\x14\x15*'
   >>> 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"
     被打包为二进制存储的同质数据。

  "xdrlib" 模块
     打包和解包 XDR 数据。


类
==

"struct" 模块还定义了以下类型：

class struct.Struct(format)

   返回一个新的 Struct 对象，它会根据格式字符串 *format* 来写入和读取
   二进制数据。 一次性地创建 Struct 对象并调用其方法相比使用同样的格式
   调用 "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 版更改: 格式字符串类型现在是 "str" 而不再是 "bytes"。

   size

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