如何在Python中自定义格式打印Protobuf消息?
解决Protobuf中MAC/IP地址字段可读性打印问题
当Protobuf消息中的bytes类型字段存储MAC、IP这类地址时,默认打印会输出转义的字节序列(比如"\000\016\037\204\222\333"),可读性极差。如果不想为每个消息单独编写打印函数,可以通过以下通用方案解决:
方案1:自定义通用格式化函数(简单直接)
编写一个遍历消息字段的通用函数,根据字段名或类型自动格式化MAC/IP地址:
import health_pb2 from google.protobuf.descriptor import FieldDescriptor def format_protobuf_msg(msg): output_lines = [] # 遍历消息的所有字段和值 for field, value in msg.ListFields(): if field.type == FieldDescriptor.TYPE_BYTES: # 根据字段名识别MAC地址 if "mac_addr" in field.name: mac_str = ":".join(f"{byte:02x}" for byte in value) output_lines.append(f'{field.name}: "{mac_str}"') # 根据字段名识别IPv4地址 elif "ip_addr" in field.name: ip_str = ".".join(str(byte) for byte in value) output_lines.append(f'{field.name}: "{ip_str}"') # 其他bytes字段保留默认格式 else: output_lines.append(f'{field.name}: {repr(value)}') else: # 非bytes字段按默认格式输出 output_lines.append(f'{field.name}: {value}') return "\n".join(output_lines) # 使用示例 if __name__ == "__main__": health = health_pb2.Health() health.device_uptime = 17 health.mac_addr = bytes.fromhex("000e1f8492db") print(format_protobuf_msg(health))
这个方案依赖字段名的统一命名规则(比如包含mac_addr、ip_addr),适合字段命名规范的项目。
方案2:自定义TextFormat访问器(支持嵌套消息,更原生)
基于Protobuf原生的text_format模块,自定义访问器重写bytes字段的处理逻辑,支持嵌套消息等复杂结构:
from google.protobuf import text_format from google.protobuf.descriptor import FieldDescriptor import health_pb2 class AddressFormatVisitor(text_format.PrintVisitor): def VisitBytes(self, value, field): # 识别MAC地址字段并格式化 if "mac_addr" in field.name: formatted_mac = ":".join(f"{byte:02x}" for byte in value) return f'"{formatted_mac}"' # 识别IPv4地址字段并格式化 elif "ip_addr" in field.name: formatted_ip = ".".join(str(byte) for byte in value) return f'"{formatted_ip}"' # 其他bytes字段使用默认处理逻辑 return super().VisitBytes(value, field) def print_formatted_msg(msg): visitor = AddressFormatVisitor() text_format.Print(msg, visitor=visitor) print(visitor.ToString()) # 使用示例 if __name__ == "__main__": health = health_pb2.Health() health.device_uptime = 17 health.mac_addr = bytes.fromhex("000e1f8492db") print_formatted_msg(health)
方案3:结合Protobuf自定义字段选项(最灵活可靠)
如果字段命名不统一,可以在proto文件中定义自定义字段选项,标记哪些字段是MAC/IP地址,实现更精准的识别:
1. 修改proto文件,添加自定义选项
import "google/protobuf/descriptor.proto"; // 定义自定义字段选项 extend google.protobuf.FieldOptions { bool is_mac_addr = 50001; // 标记该字段为MAC地址 bool is_ipv4_addr = 50002; // 标记该字段为IPv4地址 } message Health { bytes mac_addr = 1 [(is_mac_addr) = true]; uint32 device_uptime = 2; // 其他字段... }
2. 在Python中使用自定义选项处理字段
from google.protobuf import text_format import health_pb2 class AddressFormatVisitor(text_format.PrintVisitor): def VisitBytes(self, value, field): # 通过自定义选项判断是否为MAC地址 if field.GetOptions().Extensions[health_pb2.is_mac_addr]: formatted_mac = ":".join(f"{byte:02x}" for byte in value) return f'"{formatted_mac}"' # 通过自定义选项判断是否为IPv4地址 elif field.GetOptions().Extensions[health_pb2.is_ipv4_addr]: formatted_ip = ".".join(str(byte) for byte in value) return f'"{formatted_ip}"' return super().VisitBytes(value, field) # 使用方式同方案2
这种方案不依赖字段名,通过显式标记实现精准处理,适合大型或字段命名不统一的项目。
内容的提问来源于stack exchange,提问作者Abdurrahman Uslu
相关产品推荐
相关产品推荐

