12 Protobuf数据处理
作者:沈园园
一、PROTOBUF 数据处理简介
Protobuf(Protocol Buffers)是 google 开发的一种数据描述语言,它能够将结构化的数据序列化,并且可以将序列化的数据进行反序列化恢复原有的数据结构。一般用于数据存储以及通信协议方面。
它类似于 JSON,但更小更快,并且可以生成原生语言绑定。您只需定义一次数据结构,然后可以使用特定生成的源代码,轻松地在各种数据流中以及使用各种语言读写结构化数据。
Protobuf 解决了哪些问题?
Protobuf 提供了一种适合用于几兆字节大小的类型化、结构化数据包的序列化格式。该格式适用于瞬时网络流量和长期数据存储。Protobuf 可以扩展新的信息,而不会使现有数据无效,也不需要更新代码。
具体细节可以参考官方文档:https://protobuf.dev/overview/
二、注意事项
1、protobuf 核心库支持 Proto2 和 Proto3 协议版本;
2、person.proto 文件是消息结构定义文件(Schema),它是人类可读的文本文件,使用 Protobuf IDL 语法,该文件定义了消息类型 Person 的结构(字段名、类型、编号等),文件中示例内容如下:
syntax = "proto2";
message Person {
optional string name = 1;
optional int32 id = 2;
optional string email = 3;
}
该文件需要由开发者手动创建编写,语言规范参考:
Protocol Buffers 官方语言指南(proto3):Language Guide (proto 3) | Protocol Buffers Documentation ;
Protocol Buffers 官方语言指南(proto2):Language Guide (proto 2) | Protocol Buffers Documentation ;
3、person.pb 文件是 Schema 描述的二进制文件(FileDescriptorSet),是 person.proto 经过 protoc -o 编译后生成的二进制元数据,该文件包含 Person 消息的完整结构描述(字段名、编号、类型),用于被 protobuf.load() 加载,供后续 protobuf.encode() 和 protobuf.decode() 能知道 Person 的结构,从而正确编解码;
4、关于如何得到 person.pb 文件说明如下:
- 需要先下载 protoc.exe,下载链接:https://github.com/protocolbuffers/protobuf/releases ;
- 在 protoc.exe 文件目录下打开 CMD,输入 protoc -o person.pb person.proto 即可生成 person.pb 文件,前提需要同目录下包含 person.proto 文件;
5、person.pbtxt 文件是用户数据的文本表示(TextFormat),它是人类可读的文本文件,表示一个具体的 Person 消息实例,内容是字段赋值,文件中示例内容如下:
name: "wendal"
id: 123
email: "abc@qq.com"
该文件需要用户手动编写(用于配置、测试),或者通过程序把用户数据以人类可读的文本格式保存在 .pbtxt 文件下;
该文件内容需要遵循 Protobuf 文本格式语法规范(Text Format Syntax),语言规范参考:
Protobuf Text Format 官方语言规范(草案):Text Format Language Specification | Protocol Buffers Documentation ;
6、person.pbtxt 文件用于调试、写测试用例、作为配置文件,可被支持 TextFormat 的 Protobuf 库解析为内存对象,在 LuatOS 中不使用 .pbtxt 文件(protobuf 库只支持 binary encode/decode,也就是 .pb 文件);
三、演示功能概述
1、main.lua:主程序入口;
2、protobuf_app.lua:protobuf 编解码功能模块;
四、准备硬件环境

1、Air780EGH核心板一块
2、TYPE-C USB数据线一根
3、Air780EGH核心板和数据线的硬件接线方式为
- Air780EGH核心板通过TYPE-C USB口连接TYPE-C USB 数据线,数据线的另外一端连接电脑的USB口;
- 核心板正面的 ON/OFF 拨动开关 拨到ON一端;
五、准备软件环境
5.1 软件环境
在开始实践本示例之前,先筹备一下软件环境:
1、烧录工具:Luatools 下载调试工具
2、内核固件:Air780EGH最新版本的内核固件
3、脚本文件:https://gitee.com/openLuat/LuatOS/tree/master/module/Air780EHM_Air780EHV_Air780EGH/demo/protobuf
4、lib脚本文件:使用Luatools烧录时,勾选 添加默认lib 选项,使用默认lib脚本文件
准备好软件环境之后,接下来查看如何烧录项目文件到Air780EGH核心板,将本篇文章中演示使用的项目文件烧录到780EGH核心板中。
5.2 API 介绍
protobuf 核心库:https://docs.openluat.com/osapi/core/protobuf/
六、程序结构
protobuf/
│── main.lua
│── protobuf_app.lua
│── person.pb
│── person.pbtxt
│── person.proto
│── readme.md
6.1 文件说明
main.lua:主程序入口文件。protobuf_app.lua:protobuf 编解码功能模块。person.pb:二进制文件,用于被 protobuf.load() 加载,详细说明见第二部分注意事项。person.pbtxt:文件用于调试、写测试用例,详细说明见第二部分注意事项。person.proto:消息结构定义文件,详细说明见第二部分注意事项。
七、代码详解
7.1 main.lua
主程序文件 main.lua 是整个项目的入口点。它负责初始化系统环境。
7.2 protobuf_app.lua
protobuf 编解码功能模块。
7.2.1 加载 protobuf 定义文件
-- 加载 pb 文件, 这个是 proto 经过 protoc.exe 编译后生成的二进制文件
-- 下载资源到模块时不需要下载 proto 文件
-- 转换命令: protoc -o person.pb person.proto
-- protoc.exe 下载地址: https://github.com/protocolbuffers/protobuf/releases
local pb_file = "/luadb/person.pb"
local tbdata = {
name = "wendal",
id = 123,
email = "abc@qq.com"
}
if io.exists(pb_file) then
local success, bytesRead = protobuf.load(io.readFile(pb_file))
if not success then
log.info("protobuf", "加载 protobuf 定义失败,已读取 " .. bytesRead .. " 字节")
return
else
log.info("protobuf", "加载 protobuf 定义成功,共解析 " .. bytesRead .. " 字节")
end
else
log.info("protobuf", "pb 文件不存在")
return
end
7.2.2 编码数据
-- 编码数据;
local pbdata = protobuf.encode("Person", tbdata)
if pbdata then
-- 编码成功,编码后的数据通常包含不可见字符;
-- 打印长度和十六进制内容(便于调试);
log.info("protobuf", "编码成功,数据长度:" .. #pbdata)
log.info("protobuf", "十六进制内容:" .. pbdata:toHex())
else
log.info("protobuf", "编码失败:数据格式或类型不匹配")
end
-- 对比 protobuf 编码和 json 编码的大小;
local jdata = json.encode(tbdata)
if jdata then
log.info("json", "编码成功,数据长度:" .. #jdata)
log.info("json", "数据内容:" .. jdata)
else
log.info("json", "编码失败:数据格式或类型不匹配")
end
-- 可见 protobuffs 比 json 节省很多空间;
7.2.3 数据解码
-- 数据解码;
local tbdata = protobuf.decode("Person", pbdata)
if tbdata then
-- 解码后的数据为 Lua table 格式,需要转化为 json 进行显示;
log.info("protobuf", "解码成功,数据内容:", json.encode(tbdata))
else
log.info("protobuf", "解码失败")
end
7.2.4 清除所有已加载的定义数据
-- 清除所有已加载的定义数据;
protobuf.clear()
log.info("protobuf", "所有 protobuf 定义已清除")
八、运行结果展示
出现类似于下面的日志,就表示运行成功:
[000000000.744] I/user.main protobuf 001.000.000
[000000000.753] I/user.protobuf 加载 protobuf 定义成功,共解析 85 字节
[000000000.753] I/user.protobuf 编码成功,数据长度:22
[000000000.754] I/user.protobuf 十六进制内容:0A0677656E64616C107B1A0A6162634071712E636F6D
[000000000.754] I/user.json 编码成功,数据长度:47
[000000000.754] I/user.json 数据内容:{"name":"wendal","id":123,"email":"abc@qq.com"}
[000000000.755] I/user.protobuf 解码成功,数据内容: {"name":"wendal","id":123,"email":"abc@qq.com"}
[000000000.755] I/user.protobuf 所有 protobuf 定义已清除
九、总结
通过本文学习,你可以学习到protobuf 编解码功能。