@danor-lib/biffer
v3.1.0
Published
Binary data unpack library based on Node.js Buffer
Readme
[!TIP] 本文档由 AI 阅读代码后生成。中文版本已经过本人初步审校。英文版本基于中文版本由 AI 翻译生成。如有问题或更好的文档,欢迎提交 Issue。
This document is generated by AI after reading the code. The Chinese version has been preliminarily reviewed by the author. The English version is AI-translated based on the Chinese version. If you have any issues or a better version of the documentation, please feel free to submit an Issue.
@danor-lib/biffer
一个用于 Node.js 的轻量级二进制数据解析与缓冲区操作工具,灵感来源于 Python 的 struct 模块。
A lightweight binary data parsing and buffer manipulation tool for Node.js, inspired by Python's struct module.
Biffer 提供了一种声明式的方式来解包(unpack)二进制数据,同时封装了 Buffer 和文件描述符(fd)的读写操作,使得处理二进制文件或网络数据流更加便捷。
基础示例
import Biffer from '@danor-lib/biffer';
// --- 使用 Buffer ---
const buffer = Buffer.from([0x01, 0x02, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f]);
const biffer = new Biffer(buffer);
const [num, str] = biffer.unpack('<Hxx4s');
console.log(num); // 513 (0x0201 小端序)
console.log(str); // 'Hell'
// --- 使用文件路径 ---
const fileBiffer = new Biffer('./data.bin');
const header = fileBiffer.unpack('>4sI'); // 读取 4 字节字符串和 1 个无符号整数
console.log(header);
fileBiffer.close(); // 记得关闭文件描述符核心概念
Biffer 围绕一个核心的“格式字符串”(struct)来工作。格式字符串描述了一段二进制数据的结构,类似于 C 语言的结构体或 Python struct 的格式字符。
| 字符 | 类型 | 大小 (字节) | 含义 |
| :--- | :----------------- | :---------- | :-------------------------- |
| < | - | - | 小端字节序(默认) |
| > | - | - | 大端字节序 |
| x | padding | 1 | 填充字节(读取时跳过) |
| c | char | 1 | 字符(返回字符串) |
| s | char[] | 1 | 字符串(指定长度,如 4s) |
| b | signed char | 1 | 有符号字符 |
| B | unsigned char | 1 | 无符号字符 |
| h | short | 2 | 有符号短整数 |
| H | unsigned short | 2 | 无符号短整数 |
| i | int | 4 | 有符号整数 |
| I | unsigned int | 4 | 无符号整数 |
| l | long | 4 | 有符号长整数 |
| L | unsigned long | 4 | 无符号长整数 |
| q | long long | 8 | 有符号长长整数 |
| Q | unsigned long long | 8 | 无符号长长整数 |
| f | float | 4 | 单精度浮点数 |
| d | double | 8 | 双精度浮点数 |
格式字符串支持数量前缀,例如 4s 表示 4 字节的字符串,2i 表示两个连续的整数。
静态方法
这些方法无需创建实例即可调用,主要用于计算格式长度或进行一次性解析。
Biffer.calc(struct)
计算指定格式字符串对应的二进制数据总长度(字节数)。
- 参数:
struct{string}- 格式字符串。
- 返回值:
{number}- 总字节数。
const size = Biffer.calc('<4sI2b');
console.log(size); // 10 (4 + 4 + 1 + 1)Biffer.unpack(struct, buffer, cursor)
静态版本的数据解包方法,不依赖实例,直接对传入的 Buffer 进行操作。
- 参数:
struct{string}- 格式字符串。buffer{Buffer}- 要解析的 Buffer。cursor{number}可选- 开始读取的位置,默认为0。
- 返回值:
{Array<number|bigint|string>}- 解包后的数据数组。
const buffer = Buffer.from([0x41, 0x42, 0x00, 0x00, 0x01]);
const [str, num] = Biffer.unpack('2sH', buffer);
console.log(str); // 'AB'
console.log(num); // 1 (小端序 0x0001)构造函数与实例属性
new Biffer(raw)
创建一个 Biffer 实例,可以包装一个 Buffer、一个文件描述符(fd)或一个文件路径。
- 参数:
raw{Biffer | Buffer | string | number}Biffer实例:克隆一个新的实例(共享底层引用或文件描述符)。Buffer实例:直接基于该 Buffer 进行操作。string:文件路径,内部将调用openSync打开。number:已存在的文件描述符(fd)。
| 类型 | 作用 |
| :-------------- | :---------------------------------------- |
| Biffer | 克隆实例,光标独立,但共享底层 target。 |
| Buffer | 基于内存中的 Buffer 进行读写。 |
| string (路径) | 打开文件,支持大文件的分块读取。 |
| number (fd) | 直接使用已打开的文件描述符。 |
实例属性
| 属性 | 类型 | 描述 |
| :-------------------- | :--------------- | :---------------------------------------------------------------------------- |
| cursor (getter) | number | 当前读写的字节位置。可通过 seek 和 skip 修改。 |
| length (getter) | number | 底层 target 的总长度。对于文件,是文件大小;对于 Buffer,是 Buffer 的长度。 |
| target (getter) | Buffer\|number | 获取底层的原始对象(Buffer 或文件描述符)。 |
| usingFileDescriptor | boolean | 指示当前实例是否操作文件描述符。 |
| path | string | 如果是通过文件路径构造的实例,该属性保存文件路径。 |
实例方法
unpack(struct)
从当前光标位置开始,根据格式字符串解析数据,并自动将光标向后移动相应的字节数。这是 Biffer 最核心的实例方法。
- 参数:
struct{string}- 格式字符串。
- 返回值:
{Array<number|bigint|string>}- 解包后的数据数组。
const biffer = new Biffer('./data.bin');
const magic = biffer.unpack('4s'); // 读取文件头标识
const version = biffer.unpack('I');
console.log(`Magic: ${magic[0]}, Version: ${version[0]}`);slice(size, options)
从当前光标位置提取一段数据。如果是基于文件的实例,该方法会从磁盘读取;如果是基于 Buffer 的实例,则直接创建子视图。
- 参数:
size{number}- 要提取的字节数。options{Object}可选- 配置项。wrap{boolean}- 是否将结果包装为新的Biffer实例。默认为true。seek{boolean}- 是否在提取后自动移动光标。默认为true。
- 返回值:
{Biffer | Buffer}- 如果wrap为true返回新Biffer实例,否则返回Buffer。
// 假设文件前 8 字节是头,后面是 16 字节的数据块
const headerBiffer = fileBiffer.slice(8); // 返回一个新的 Biffer,光标推进 8 字节
const rawBlockBuffer = fileBiffer.slice(16, { wrap: false }); // 返回 Buffer,光标推进 16 字节seek(cursor)
将光标移动到指定位置。
- 参数:
cursor{number}- 绝对位置。
- 返回值:
{number}- 移动后的光标位置。
skip(offset)
将光标向后(或向前)移动指定的偏移量。
- 参数:
offset{number}- 相对移动量(负数表示向前移动)。
- 返回值:
{number}- 移动后的光标位置。
tell()
获取当前光标位置。等同于访问 instance.cursor。
- 返回值:
{number}- 当前光标位置。
find(data)
从当前光标位置开始,向后搜索指定的数据(Buffer 或可转为 Buffer 的数据),并返回其首次出现的绝对位置。如果找到,光标将自动移动到该位置。
对于文件描述符实例,该方法会分块读取文件(每次 1MB + 搜索数据长度)进行搜索,避免将整个大文件加载到内存。
- 参数:
data{any}- 要搜索的数据,内部会通过Buffer.from(data)转换。
- 返回值:
{number}- 找到的位置索引,如果未找到则返回-1。
const positionPNG = fileBiffer.find('PNG'); // 搜索 'PNG' 标识
if (positionPNG !== -1) {
console.log(`Found PNG at offset: ${positionPNG}`);
}findFromHead(data)
等价于 biffer.seek(0) 后立即调用 biffer.find(data)。即从文件开头搜索数据。
- 参数:
data{any}- 要搜索的数据。
- 返回值:
{number}- 找到的位置索引,如果未找到则返回-1。
unpackString(charLength)
一个便捷方法,用于解析带有长度前缀的字符串。常见于许多二进制格式(如 Pascal 字符串或带长度头的字符串块)。
- 参数:
charLength{string}可选- 表示长度字段的格式字符,默认为'L'(4 字节无符号长整数)。
- 返回值:
{string}- 解析出的字符串。
// 假设数据布局: [4字节长度][指定长度的字符串数据]
const stringData = fileBiffer.unpackString('I');
console.log(stringData);isReach()
检查光标是否已经到达或超过了数据的末尾。
- 返回值:
{boolean}- 如果cursor >= length则返回true。
while (!biffer.isReach()) {
const chunk = biffer.unpack('H');
// 处理数据...
}clone()
创建一个新的 Biffer 实例,该实例共享相同的底层 target(Buffer 或文件描述符),但拥有独立的光标位置。
- 返回值:
{Biffer}- 新的实例。
close()
关闭底层的文件描述符(如果实例是通过文件路径或文件描述符创建的)。基于 Buffer 的实例调用此方法无操作。建议在完成文件操作后始终调用此方法以释放系统资源。
高级特性说明
1. 字节序(Endianness)
- 默认字节序为小端(
LE)。 - 在格式字符串的开头放置
>或<可以显式指定字节序。 - 字节序标志仅对多字节类型(如
H、I、f等)生效,单字节类型(c、s、B)不受影响。
const biffer = new Biffer(Buffer.from([0x00, 0x01]));
console.log(biffer.unpack('>H')[0]); // 大端序:256
biffer.seek(0);
console.log(biffer.unpack('<H')[0]); // 小端序:12. 处理大文件
使用文件路径构造实例时,Biffer 不会一次性将文件读入内存。slice、find 和 unpack 等方法均基于 readSync 按需读取,因此可以高效处理 GB 级别的文件。
3. 填充字节(Padding)
格式字符 x 用于跳过指定数量的字节,不产生返回值。这在你需要忽略结构体中的对齐填充时非常有用。
// 假设结构为: char (1B) + padding (3B) + int (4B)
const [char, int] = Biffer.unpack('c3xi', buffer);