在前端开发中,经常会涉及到二进制数据的解析,而 npm 包 bitparser 就是一个可以方便解析二进制数据的工具。本文将介绍 bitparser 的基本用法和实际应用场景。
什么是 bitparser?
bitparser 是一个可以将二进制数据转换为 JavaScript 对象的 npm 包。它可以用于解析包括位字段,重复字段和定长字段在内的各种数据类型。其主要特点包括:
- 可以自定义编解码规则
- 支持联合和枚举类型
- 可以从二进制数据中提取指定位的值
- 代码简单易用
bitparser 可以用在许多场景下,比如:
- 解析网络协议
- 处理嵌入式设备发送的数据
- 解析二进制日志文件
接下来,我们就来实际操作一下 bitparser。
安装
在使用 bitparser 之前,需要先安装它。可以使用以下命令进行安装:
npm install bitparser --save
安装成功后,可以将 bitparser 引入你的代码中:
const bitparser = require("bitparser");
基本使用
假设我们有一个二进制文件,用以下结构体表示:
struct Header { uint8_t type; uint16_t length; };
其中,type 字段占了 1 个字节,length 字段占了 2 个字节。现在我们想要将这个结构体从二进制文件中解析出来,可以这样写:
const bp = bitparser.parse("uint8 type, uint16 length"); const buf = Buffer.from([0x01, 0x00, 0x04]); const obj = bp.read(buf); console.log(obj); // { type: 1, length: 4 }
在这段代码中,我们先定义了一个解析模板字符串 "uint8 type, uint16 length",它告诉 bitparser 需要解析出一个名为 type 的 uint8_t 类型字段和一个名为 length 的 uint16_t 类型字段。然后,我们将这个解析模板传给 bitparser.parse() 方法,生成一个解析器 bp。接着,我们将一个值为 [0x01, 0x00, 0x04] 的 Buffer 传给 bp.read() 方法,得到解析后的 JavaScript 对象。
更高级的用法
bitparser 还支持许多更高级的用法,让我们一一来看:
自定义编解码规则
bitparser 默认支持几种常见的数据类型,例如 uint8、uint16、uint32、int8、int16、int32、float32、float64、bool、string 等。但有时候,我们需要解析一些自定义的数据类型,例如 IPv4 地址、日期时间、压缩整数等。这时候,我们需要自定义编解码规则(coder)。
自定义编解码规则的步骤如下:
- 定义编码规则:将 JavaScript 对象转换为二进制数据
- 定义解码规则:将二进制数据转换为 JavaScript 对象
下面是一个自定义数值型字段解析器的例子:
-- -------------------- ---- ------- ----- ----------- - - ------- -------- ----- - ----- --- - ---------------- -------------------- -- --- ------ ---- -- ------- -------- ----- - ------ ----------------- --- - --
这个编解码器用于处理 24 位无符号整数的编解码。其中,encode 函数将一个数值类型的值 num 转换为二进制数据 buf,而 decode 函数则将二进制数据 buf 转换为 JavaScript 对象。
上面的例子中,encode 函数使用了 Buffer.alloc() 方法创建了一个长度为 3 的 Buffer,然后使用 buf.writeUIntBE() 方法将 num 写入 buf 中。而 decode 函数则使用了 buf.readUIntBE() 方法将 buf 中的值读取出来。
有了这个编解码器,我们就可以使用以下解析模板字符串解析一个 24 位无符号整数了:
const bp = bitparser.parse("coder <uint24> data", { coders: { uint24: uint24Coder } }); const buf = Buffer.from([0x00, 0xff, 0xff]); const obj = bp.read(buf); console.log(obj); // { data: 16777215 }
在解析模板字符串中,我们使用了 coder <uint24> 格式来定义一个名为 data 的字段,它使用 uint24Coder 编解码器进行解析。
对象嵌套和数组解析
有时候,二进制数据中的字段并不是简单的数值类型,而是一个嵌套的对象或者一个数组。这时候,我们可以使用对象嵌套和数组解析来实现。
下面是一个嵌套结构体的例子:
-- -------------------- ---- ------- ------ ----- - ----- -- ----- -- -- ------ ---- - ----- ------ ----- ---- --
我们可以使用以下解析模板字符串实现对这个结构体的解析:
-- -------------------- ---- ------- ----- -- - ---------------- ---- ------- ------ --- ------- ----- - ------- - ------ - ------- -------- ----- - ----- --- - ---------------- ----------------------- --- ----------------------- --- ------ ---- -- ------- -------- ----- - ----- - - ------------------- ----- - - ------------------- ------ - -- - -- - - - - -- ----- --- - ------------- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ---- --- ----- --- - ------------- ----------------- -- - ------ - -- ------------------- -- - -- ---- - -- -------------------- -- -- - -
可以看到,在解析模板字符串中,我们使用 obj <point> 格式来定义一个名为 Point 的对象类型。而在 Point 对象的编解码器中,我们使用了 buf.writeFloatBE() 和 buf.readFloatBE() 方法,将 x 和 y 字段依次写入和从 buf 中读取出来。
类似地,我们可以使用以下解析模板字符串解析一个包含多个 Point 对象的数组:
-- -------------------- ---- ------- ----- -- - ---------------- ------ ------- ----- -------- - ------- - ------ - -- ----- ----- -- -- ----- - ------- -------- ----- - ----- --- - ----------------- ----------------------------- --- ----------------------------- --- --------------------------- --- --------------------------- ---- ------ ---- -- ------- -------- ----- - ----- ----- - - -- ------------------- -- ------------------ -- ----- --- - - -- ------------------- -- ------------------- -- ------ - ------ --- -- - - - - -- ----- --- - ------------- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ---- --- ----- --- - ------------- ----------------- -- - ------ - - ------ - -- ------------------- -- - -- ---- - -- -------------------- -- -- - -- - ------ - -- ------------------- -- - -- ---- - -- -------------------- -- -- - - - -
在解析模板字符串中,我们使用了 array <point> 格式来定义一个 Point 类型的数组。而在每个数组元素的编解码器 Line 中,我们依次读取和写入了四个 Point 类型的字段。
从二进制数据中提取指定位的值
有时候,我们只需要从解析出来的字段中提取其中的一部分,这时候,可以使用 bitparser.BitField 类型。BitField 表示一个包含在一个整数类型字段中的二进制位集合。
例如,假设我们有一个二进制文件,其中包含一个名为 flags 的 uint8_t 类型字段,其中各个位的含义如下:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
值1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
我们可以使用以下解析模板字符串解析这个字段:
const bp = bitparser.parse("uint8 flags"); const buf = Buffer.from([0x01]); const obj = bp.read(buf); console.log(obj); // { flags: 1 }
这样,我们就可以将这个字段解析出来了。但如果我们只需要其中的某一个二进制位,比如第六位,可以使用 bitparser.BitField 类型:
const bp = bitparser.parse("bits <1> otherFlag, uint8 flags"); const buf = Buffer.from([0x29]); const obj = bp.read(buf); console.log(obj); // { otherFlag: 1, flags: 41 }
在解析模板字符串中,我们使用了 bits <1> 格式来定义一个一个只包含一个二进制位(即一位二进制数)的 BitField 类型字段。在实际解析的时候,我们使用了一个名为 otherFlag 的字段来存储这一位的值,通过查看解析结果可以发现它的值为 1。而 flags 字段则包含了整个 uint8_t 类型字段的值,在本例中为 41(其二进制表示为 0b00101001)。
结语
通过本教程,我们了解了 bitparser 的基本用法和更高级的用法,并且通过实例代码介绍了如何自定义编解码器、嵌套解析对象和数组、以及从二进制数据中提取指定位的值。希望它能对你在解析二进制数据时产生帮助。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/5eedacc8b5cbfe1ea0610b22