WXF 格式说明

WXF 是一种二进制格式,忠实地把 Wolfram 语言表达式序列化成适于外部存储或可与其他程序交换的形式. WXF 可使用许多编程语言中的低级原始类型进行诠释,使之可在其他编程语言中读取或编写 Wolfram 语言表达式.
Wolfram 语言表达式和其序列化格式间转换的基本函数是 BinarySerializeBinaryDeserialize. 对 WXF 数据的文件的读取和编写的支持也内置于 ExportImport.
BinarySerialize[expr]
给出 WXF 格式中任何表达式 expr 的二进制表示
BinaryDeserialize[bytearray]
WXF 格式中的二进制表示恢复一个表达式
Import[file,"WXF"]
导入一个 WXF 文件并返回一个表达式
Export[file,expr,"WXF"]
序列化任意表达式并存为一个 WXF 文件
ImportByteArray[ba,"WXF"]
导入数据并返回一个表达式
ExportByteArray[expr,"WXF"]
产生一个 ByteArray 对象对应于导出在 WXF 格式的 expr
在 Wolfram 语言中有很多种方式序列化和反序列化 WXF.

基本结构

WXF 格式中的数据总是包含纯 ASCII 标头紧跟着一个字节字符串. 标头指明如何解码字节并用冒号分隔表示成分序列的字节字符串.
通过给出成分序列继续字节数组,每个用下列列表中的一个令牌开始指定成分的类型.
字节值
字符表示 (ISO8859-1)
成分类型
102"f"
函数
67"C"
带符号 8 位整数
106"j"
带符号 16 位整数
105"i"
带符号 32 位整数
76"L"
带符号 64 位整数
114"r"
IEEE 双精度实数
83"S"
字符串
66"B"
二进制字符串
115"s"
符号
73"I"
大整数
82"R"
大实数
193"Á"
打包数组
194"Â"
数值数组
65"A"
关联
58":"
关联中的延迟规则
45"-"
关联中的规则
WXF 令牌的详尽列表.
令牌之后,如果有必要的话,是长度规范,然后是成分实际内容元素的序列.

基本范例

给出符号序列化格式的字节:
将字节显示为 "ISO8859-1" 的字符:
以字符形式给出字符串序列化格式的字节:
Range[10] 的序列化格式给出字节:
创建一个函数标签数组部分:
首两个字节属于标头,剩下的对应于压缩数组 (packed array):
带有版本的标头后面是分隔符:
带有类型、秩和维度以及数据的压缩数组:

多成分的范例

上面章节的例子主要包括单个成分. 令牌列表的单个令牌,接下来是尺寸信息和数据. 本章节的例子包含多个成分和类型. 第一个例子通过以下交互式插图显示:
首两个字节是标头和冒号分隔符:
下一个字节是函数的令牌和参数数目:
下一个字节是函数标头的令牌,以下这种情况是符号:
再之后就是符号 UTF-8 表示的字节数:

因此,接下来 6 个字节是符号的 UTF-8 表示:
完整的标头被读取,下一个字节是第一个参数的令牌:
如果这个参数是一个符号,下一个字节是它的长度和名字:
函数只有一个参数,因此现在所有字节必须已被读取:
第二个范例是三个元素的列表. 该列表表示长度为 3 的函数,其中,标头 List 紧跟 3 个成分. 第一个成分引入了用于表示紧密格式的整数格式,使用一个令牌和一个 "Integer8". 最后部分显示一个 ByteArray 的表示:
给出 {1,-1,ByteArray[{1,2,3}]} 序列化格式的字节:
忽略标头,该表达式是三个参数的函数:
标头是四个字节的符号,即 List
第一个参数标头之后是 8 位整数 1
下两个字节是第二个参数,即 8 位整数 -1
使用 Mod 诠释字节为带符号的值,把 255 转换成期望的 -1
第三个和最后一个参数是长度为 3 的二进制字符串,值为 {1,2,3}

更多关于标头

标头是变长度的纯 ASCII 字符串,由字符 ":" 分割. 对于 WXF 的当前版本 (1.0),标头的第一个字节是字符 "8"(例如,字节值 56). 当二进制序列化被 zip 压缩,这会通过字符 "C" 在标头指明. 标头永远不会被压缩;压缩只适用于接下来的字节字符串.
序列化一个表达式并给出字节数组:
首字节是字符 8:
压缩由标头的第二个字符 "C" 指出:

长度编码 (Varint)

WXF 类型有三类:
WXF 中,所有表示长度或尺寸的整数均使用变式 (varint) 方法序列化. 变式是自我表明的变量长度格式,其中,更小的整数需要更少的字节. 除最后一个字节外的每个字节都设置了最高有效位 (MSB). MSB 指示流的后续字节是否也是变式的一部分,作为延续标记. 每个字节的低 7 位存储整数的二进制表示,最低有效组首先存储.
下面是构建 500 的变式表示的例子:
500 的基数 2 数字:
按 7 位分组,最低有效位先:
排序每组的位,最高有效位先,填充每组使每组有 8 位:
设置每个字节的首位,除了最后一个,获取变式的二进制格式:
根据基数 2 数字构建字节值,给出 500 的变式表示:
下图说明了把 varint 编码的字节序列 {244,3} 解码回 500 的逆操作,其中,每个初始 varint 位有一个唯一颜色:

字符串、符号和非机器数

使用同样的格式表示字符串、符号和非机器数. 第一个字节是一个令牌,然后字节数编码成 varint 格式,接下来是 Unicode 字符的字符串,对应于表达式的字符串 InputForm. 非机器数可以是任意精度的实数或整数,其需要比 $SystemWordLength 位数更长的表示.
原子类型
令牌
表示
String"S"
Unicode 字符序列
Symbol"s"
符号完全符合的名称,指定上下文,除了 System` 符号
Arbitrary-precision reals"R"
数字表示指定尾数和最终的精度和指数
Big integers"I"
数字字符串
基于 InputForm 的类型.
序列化 Alice in Wonderland 中前 500 个字符:
标头后面的首字节是字符串的令牌:
下两个字节是 varint 编码的 500,出现在之前的范例中:
剩下的字节是 UTF-8 中的字符串内容:
序列化非机器数:
除了令牌外,序列化与字符串一样:
更小的数字只需要一个字节来编码长度:

机器整数序列化

由下面列表的最小整数类型识别的机器整数可表示值,然后是整数的二进制补码. 字节顺序总是小端 (little endian).
令牌
定义
类型大小
"C"signed 8-bit integer
"j"signed 16-bit integer
"i"signed 32-bit integer
"L"signed 64-bit integer
整数令牌,它们相关的类型和被每个表达式使用的字节数.
对应于 2^14 序列化的字节:
跳过俩字节标头并以字符形式显示第一个令牌:
查看令牌后的字节:
把先前的字节对转换成整数:
负整数二进制表示使用二进制补码方法. 给出 N 位整数 α,它的二进制补码 β 是关于 2N: α+β=2N 的补码. 数字取反是通过二进制补码获得.
8 位整数 1 的二进制补码:
二进制补码是 -1 的 8 位二进制表示:
序列化一个负的 16 位整数:
最后两个字节是整数值:
把字节对转换成十进制格式:
10000 的 16 位二进制补码的值:

机器实数序列化

机器实数使用字符 "r" 以及 IEEE 754 标准中的双浮点值的内存表示. 对于机器整数,字节排序总是低位优先 (little endian).
序列化一个实数:
跳过两个字节标头并以字节和字符显示第一个令牌:
下一个字节是 IEEE 754 标准中的实值:
机器精度复数是作为两个机器精度实数的函数序列化的. 下图高亮 Complex 标头以及紧跟着的两个实数值:
序列化一个机器复数:
标头后的第一个字节是长度为 2 的函数,其中标头为 Complex
接下来 9 个字节是实数部分并匹配实数值 4. 的序列化,如前所示:
剩下的字节是虚部,还是实数值 4.

函数序列化

WXF 中的函数由字符 "f" 表示,紧跟着 varint 格式的表达式长度. 对于标头,元素数目等于长度加 1. 标头和成分是任意序列化的表达式. 另外,标头也可以是函数:Select[OddQ][{1,2,3}] 是长度为 1 的函数,标头为 Select[OddQ],它本身是标头为 Select 且长度为 1 的函数.
序列化一个表达式,使用 Unevaluated 阻止它被计算:
首两个字节对应于长度为 1 的函数:
接下来 16 个字节是标头 Select[OddQ] 的序列化,如前所示
剩下的字节是参数,首先是令牌和长度:
紧接着是标头:
三个机器整数完成了表达式:

关联序列化

关联由字符 "A" 表示,紧跟着长度和规则.
关联规则由字符 "-" 表示,延迟规则由字符 ":" 表示. 紧跟着两个任意序列化的表达式. 关联规则的长度总是 2,超过的被省略. 以下插图显示了简单关联的序列化:
序列化关联:
首字节对应于两个元素的关联:
然后是规则含有两个部分;长度因此被省略:
延迟规则是最后的部分:
之前范例的规则是关联的一部分. RuleRuleDelayed 不是 Association 的部分被序列化为函数. 序列化表单压缩较少,显示在下一个范例中.
序列化规则列表:
首字节声明两个元素的列表:
首元素是长度为 2 的函数,标头为 Rule
规则的参数与关联的情况是一样的:
第二个元素也是长度为 2 的函数,但是它的标头是 RuleDelayed
类似,规则延迟元素保持不变:
序列化长度大致是字符串 FullForm 的大小:
关联序列化成更紧密的格式:

二进制字符串

二进制字符串由令牌 "B" 表示. 它们遵循与字符串一样的模式,但是字节序列是任意的而不是 UTF-8 字符. ByteArray 被序列化为二进制字符串.
序列化字节数组:
标头后的首字节是二进制字符串令牌,紧跟着二进制数据长度:
下一个字节是数据:
解码字节为 UTF-8 不是总是成功的:
字节总可以用 "ISO8859-1" 编码表示为一个字符串:

数值数组

数组是机器精度数值的多维表格. 数组由以下序列表示:一个指定数组类型的令牌,一个指定值类型的令牌,varint 格式中的秩,作为整数序列的维数也在 varint 格式中,最后是数据.
WXF 格式中有两种类型的数组:压缩数组由令牌 "Á" 表示(字节值 193),数值数组由令牌 "Â" 表示(字节值 194). 这两者之间有些微不同,主要是被支持的值得类型,如下表所示.
整数值
16 进制表示
数组类型
00016
8 位带符号整数数组
10116
16 位带符号整数数组
20216
32 位带符号整数数组
30316
64 位带符号整数数组(只适于 64 位系统)
342216
IEEE 单精度实数数组(float)
352316
IEEE 双精度实数数组 (double)
513316
IEEE 单精度复数数组
523416
IEEE 双精度复数数组
压缩数组的有效值类型令牌.
整数值
16 进制表示
数组类型
00016
8 位带符号整数数组
161016
8 位无符号整数数组
10116
16 位带符号整数数组
171116
16 位无符号整数数组
20216
32 位带符号整数数组
181216
32 位无符号整数数组
30316
64 位带符号整数数组
191316
64 位无符号整数数组
342216
IEEE 单精度实数数组 (float)
352316
IEEE 双精度实数数组 (double)
503316
IEEE 单精度复数数组
513416
IEEE 双精度复数数组
数值数组的有效值类型令牌.
打包数组支持的整数范围随系统单词长度,$SystemWordLength 而变化,在 32 位系统从 -231+1231-1,在 64 位系统,从 -263+1263-1. 实数的打包数组不能存储 IEEE 除了 NaNinf. 这些限制不适用于数值数组.
定义一个矩阵:
作为一个压缩数组序列化:
标头之后的首字节是压缩数组令牌:
下一个字节表明 16 位带符号的整数数组:
以下字节是数组的秩和维数:
尾字节是值:
可以重构每个 16 位整数的小数形式. 首先,将这字节对分组:
每对是小端 16 位长整数,使用位移运算重构其值:
下面的交互性插图显示了上面矩阵压缩前的序列化. 元素序列显著不同,因为它包含带有标头 List 的嵌套函数. 内部列表有三部分对应整数值. 值得注意的是整数值的二进制表示类似于压缩数组的情况(小端带符号 16 位 整数) .
数组值类型令牌被构造为位域. 四个最低有效位以字节为单位存储数值类型大小的对数,四个最高有效位表示数字类型. 请注意,对于复数类型,大小指的是整个数字,例如,单精度复数类型的大小为 8 字节.
0000
0001
0010
0011
0100
值类型令牌的四个最低有效位以及对应的类型大小与(以字节为单位).
0000
整数
0001
无符号整数
0010
实数
0011
复数
值类型令牌的四个最高有效位以及对应的数值类型.
有可能使用 Wolfram 语言构建位域对应于双精度实数数组.
双精度实数是 8 位字节长;基数为 2 的对数是 3:
寻找位的表示:
查看由对应于实数和类型大小的数值类型的级联形成的位域:
转换前置位序列检索期待的字节值: