- 定义
.proto文件:创建你的数据结构定义。 - 生成 Python 代码:使用 Protobuf 编译器 (
protoc) 将.proto文件转换成 Python 类。 - 读取/解析数据:在 Python 代码中导入生成的模块,并解析二进制数据。
第 1 步:定义 .proto 文件
你需要一个 .proto 文件来描述你的数据结构,我们以一个简单的 Person 消息为例。

创建一个名为 person.proto 的文件:
// person.proto
syntax = "proto3"; // 指定使用 Protocol Buffers 的第 3 版语法
// 定义包名,可以防止命名冲突
package tutorial;
// 定义一个 Person 消息类型
message Person {
string name = 1;
int32 id = 2; // 独特的ID号
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4; // 'repeated' 关键字表示这是一个数组/列表
}
关键点说明:
syntax = "proto3";:必须指定,推荐使用proto3。message:定义一个数据结构,类似于 Python 中的类或 C++ 中的结构体。= 1,= 2:这些是字段编号,是 Protobuf 中非常重要的概念,在定义消息后,绝对不要修改这些编号,因为它会影响数据的序列化和反序列化。repeated:表示该字段可以包含 0 个或多个值,在 Python 中会映射为list。enum:枚举类型,在 Python 中会映射为Enum类。
第 2 步:生成 Python 代码
你需要使用 protoc 编译器将 person.proto 文件转换成 Python 代码。
前提条件:安装 protoc 和 Python Protobuf 库
-
安装
protoc编译器:
(图片来源网络,侵删)- macOS (使用 Homebrew):
brew install protobuf
- Windows (使用 Scoop):
scoop install protobuf
- Linux (Debian/Ubuntu):
sudo apt-get update sudo apt-get install protobuf-compiler
- 下载安装:也可以从 Protocol Buffers 官网 下载对应系统的预编译包。
- macOS (使用 Homebrew):
-
安装 Python Protobuf 库: 在你的 Python 环境中,使用 pip 安装:
pip install protobuf
运行编译器
打开终端或命令行,进入到 person.proto 文件所在的目录,然后执行以下命令:
# -I 指定 .proto 文件所在的目录(. 表示当前目录) # --python_out 指定生成的 Python 代码的输出目录(. 表示当前目录) protoc -I=. --python_out=. person.proto
执行成功后,你会发现目录下多了一个 person_pb2.py 文件,这就是我们需要的 Python 模块,它包含了 Person 消息类的定义。
第 3 步:在 Python 中读取/解析数据
现在我们可以在 Python 脚本中使用这个生成的模块了,读取操作通常指的是解析一个已经序列化好的二进制文件。

假设我们有一个 person.bin 文件,里面存储了一个 Person 对象的二进制数据。
示例:创建并序列化数据(为了演示)
我们创建一个脚本 create_data.py 来生成这个二进制文件,这样我们后面才有数据可以读取。
# create_data.py
import person_pb2
# 1. 创建一个 Person 对象
person = person_pb2.Person()
person.name = "John Doe"
person.id = 123
person.email = "john.doe@example.com"
# 添加电话号码
phone1 = person.phones.add()
phone1.number = "123-456-7890"
phone1.type = person_pb2.Person.HOME
phone2 = person.phones.add()
phone2.number = "987-654-3210"
phone2.type = person_pb2.Person.WORK
# 2. 将对象序列化为二进制数据
serialized_data = person.SerializeToString()
# 3. 将二进制数据写入文件
with open("person.bin", "wb") as f:
f.write(serialized_data)
print("数据已序列化并写入 person.bin")
运行这个脚本:
python create_data.py
现在你的目录下应该有了 person_pb2.py 和 person.bin 两个文件。
示例:读取并解析数据
我们创建另一个脚本 read_data.py 来读取并解析 person.bin 文件。
# read_data.py
import person_pb2
# 1. 创建一个空的 Person 对象,用于填充数据
person = person_pb2.Person()
# 2. 从文件中读取二进制数据
with open("person.bin", "rb") as f:
# 3. 解析二进制数据到 person 对象中
person.ParseFromString(f.read())
# 4. 现在可以像访问普通 Python 对象一样访问数据
print("解析成功!")
print(f"Name: {person.name}")
print(f"ID: {person.id}")
print(f"Email: {person.email}")
print("\nPhones:")
for i, phone in enumerate(person.phones):
# 枚举类型需要通过 .Name() 或 .value 来获取可读的值
phone_type_str = person_pb2.Person.PhoneType.Name(phone.type)
print(f" Phone {i+1}: {phone.number} (Type: {phone_type_str})")
运行这个读取脚本:
python read_data.py
预期输出:
解析成功!
Name: John Doe
ID: 123
Email: john.doe@example.com
Phones:
Phone 1: 123-456-7890 (Type: HOME)
Phone 2: 987-654-3210 (Type: WORK)
高级用法和常见场景
从字符串解析
如果你二进制数据已经在一个 Python 字符串变量中,可以直接使用 ParseFromString。
binary_string = b'\x0a\x07\x4a\x6f\x68\x6e\x20\x44\x6f\x65\x10\x7b\x1a\x11john.doe@example.com\x22\x0c\x0a\x0c\x31\x32\x33\x2d\x34\x35\x36\x2d\x37\x38\x39\x30\x10\x01\x22\x0c\x0a\x0c\x39\x38\x37\x2d\x36\x35\x34\x2d\x33\x32\x31\x30\x10\x02' person = person_pb2.Person() person.ParseFromString(binary_string) # ... 后续处理 person ...
JSON 格式
Protobuf 默认使用二进制格式,因为它高效且体积小,但如果你需要与人或其他系统交互,JSON 是更好的选择。
前提:你需要安装 protobuf 包的额外依赖。
pip install protobuf json
序列化为 JSON:
import person_pb2
import json
# ... (假设 person 对象已经创建并填充了数据) ...
# 序列化为 JSON 字符串
json_string = json.dumps(person.__dict__) # 简单方法,但不完美
# 或者使用更通用的方法(推荐)
json_string = json.loads(person.SerializeToString()) # 这种方法不直接,需要先二进制再转
# 正确的 JSON 序列化方式需要额外库,如 `protoc-gen-json` 或手动实现。
# 一个更简单的方式是使用 `MessageToJson` (需要较新版本的 protobuf)
# from google.protobuf.json_format import MessageToJson
# json_string = MessageToJson(person)
print("JSON 格式:")
print(json_string)
从 JSON 解析:
# from google.protobuf.json_format import Parse # person_from_json = Parse(json_string, person_pb2.Person()) # 对于简单的 __dict__ 方法,可以这样反序列化(不推荐用于复杂结构) # new_person = person_pb2.Person() # new_person.ParseFromString(json.dumps(json.loads(json_string))) # 这种方法很绕,不推荐,最好使用专门的 JSON 库或官方的 json_format。
读取包含多个消息的文件
一个常见的场景是,一个文件中包含了多个连续序列化的 Protobuf 消息,你需要循环读取直到文件末尾。
# 假设 multiple_persons.bin 文件包含了多个 Person 对象的二进制数据
def read_multiple_persons_from_file(filename):
persons = []
with open(filename, "rb") as f:
while True:
# 每个消息的大小是可变的,我们需要知道下一个消息有多长
# 一个简单的方法是先读取 4 个字节作为长度(假设使用 int32)
raw_length = f.read(4)
if not raw_length:
break # 文件结束
# 将 4 字节的长度转换为整数
length = int.from_bytes(raw_length, byteorder='little', signed=False)
# 根据长度读取消息体
data = f.read(length)
if not data:
break # 文件意外结束
# 解析消息
person = person_pb2.Person()
person.ParseFromString(data)
persons.append(person)
return persons
# 使用示例
# persons_list = read_multiple_persons_from_file("multiple_persons.bin")
# for p in persons_list:
# print(p.name)
| 步骤 | 操作 | 关键代码/命令 |
|---|---|---|
| 定义 | 创建 .proto 文件,定义消息结构 |
syntax = "proto3"; message Person { ... } |
| 生成 | 使用 protoc 编译器生成 Python 代码 |
protoc -I=. --python_out=. person.proto |
| 读取 | 导入生成的 *_pb2.py 模块创建空消息实例 从文件/字符串读取二进制数据 调用 ParseFromString()访问消息字段 |
import person_pb2p = person_pb2.Person()with open(...)p.ParseFromString(data)print(p.name) |
希望这个详细的指南能帮助你掌握在 Python 中读取 Protobuf 数据!
