协议缓冲区 ParseFromString 函数未读取 Python 中的完整二进制文件

Protocol Buffer ParseFromString function not reading complete binary file in Python

我正在测试协议缓冲区并尝试读取一个 csv 文件,将其序列化并将输出写入二进制文件,然后使用 ParseFromString 读取二进制文件。我能够序列化和写入二进制文件,但是在读取时它会给出一个索引越界异常,或者在其他情况下它只输出二进制文件的最后一行,它会跳过它之前的所有内容。

我的消息很简单,有两个字段,time 和 metricusage。

syntax="proto3";

message excelData {

string time=1;
string meterusage=2;
}

序列化和写入二进制文件代码如下:

import metric_pb2 
import sys
from csv import reader
 

excel_data=metric_pb2.excelData()

with open('out.bin', 'wb') as f:
    with open('data.csv', 'r') as read_obj:
        csv_reader = reader(read_obj)
        header = next(csv_reader)
        if header != None:
            for row in csv_reader:
                excel_data.time=row[0]
                excel_data.meterusage=row[1]
                f.write(excel_data.SerializeToString())

f.close()
read_obj.close()

麻烦的部分如下:

方法一:这只是returns二进制文件的最后一行。它会跳过它之前的所有内容。

excel_data=metric_pb2.excelData()

with open('out.bin', 'rb') as f:
    content=f.read()
    excel_data.ParseFromString(content)
    print(excel_data.time)
    print(excel_data.meterusage)

方法 2:如果我读取像上面的 csv 文件一样的序列化二进制文件,它会给我一个索引超出范围的错误。我的倾向是二进制文件可能是字节数据并且不包含字符串数据类型它给出了这个错误?

使用 message.ParseFromString() 读取此二进制文件的正确方法是什么,因为通过循环读取它不起作用,也无法读取整个文件?我创建的二进制文件的快照如下:

你成功了吗?

这里有一个适合您的 hacky 解决方案(根据 streaming multiple messages 的 Protobuf 技术)将(可变!)消息长度写入每条记录之前的字节。

作家

import metric_pb2
import sys

from csv import reader

excel_data = metric_pb2.excelData()

with open('out.bin', 'wb') as f:
    with open('data.csv', 'r') as read_obj:
        csv_reader = reader(read_obj)
        header = next(csv_reader)
        if header != None:
            for row in csv_reader:
                excel_data.time = row[0]
                excel_data.meterusage = row[1]
                bytes = excel_data.SerializeToString()
                # Write the message's integer length as bytes
                f.write(len(bytes).to_bytes(1, sys.byteorder))
                # Write the message itself as bytes
                f.write(bytes)

f.close()
read_obj.close()

产生:

00000000: 1c 0a13 3230 3231 2d30 312d 3031 2030 303a 3030 3a30 3012 0535 342e 3635  ...2021-01-01 00:00:00..54.65
00000010: 1c 0a13 3230 3231 2d30 312d 3031 2030 303a 3030 3a30 3012 0535 352e 3138  ...2021-01-01 00:00:00..55.18
00000030: 1b 0a13 3230 3231 2d30 312d 3031 2030 303a 3030 3a30 3012 0435 352e 38    ...2021-01-01 00:00:00..55.8

NOTE 1c == 28 (because 54.65 and 55.18) and 1b == 27 (because 55.8)

Reader

import metric_pb2
import sys

excel_data = metric_pb2.excelData()

with open('out.bin', 'rb') as f:
    while True:
        # Read the message's length as bytes and convert it to an integer
        len = int.from_bytes(f.read(1), sys.byteorder)
        # Read that number of bytes as the message bytes
        bytes = f.read(len)
        if not bytes:
            break

        excel_data.ParseFromString(bytes)
        print("[{time}] {meterusage}".format(
            time=excel_data.time,
            meterusage=excel_data.meterusage))

产生:

[2021-01-01 00:00:00] 54.65
[2021-01-01 00:00:00] 55.18
[2021-01-01 00:00:00] 55.8
[2021-01-01 00:00:00] 56.0
[2021-01-01 00:00:00] 63.52
[2021-01-01 00:00:00] 78.1