Go 中 Varint 编码与二进制字节读取的本质区别解析

发布时间 - 2026-01-20 00:00:00    点击率:

go 中 varint 编码与二进制字节读取的本质区别解析:`binary.varint` 与 `binary.read` 行为迥异:前者按 protocol buffers 的变长整数规则解码字节流,后者则直接按指定字节序(如 littleendian)解释固定长度的原始字节,二者语义、协议和适用场景完全不同。

在 Go 标准库中,encoding/binary 包提供了两种截然不同的整数解析方式:binary.Read 和 binary.Varint。它们看似都“从字节中读取整数”,实则遵循完全不同的协议规范,不可互换使用。

? binary.Read:固定长度 + 显式字节序

binary.Read 将输入字节视为紧凑的二进制表示,严格按指定字节序(如 binary.LittleEndian)读取固定长度(例如 int64 总是读 8 字节),并直接转换为对应整数值。它不关心数据是否“编码”,只做底层字节到整数的机械映射。

以示例中的字节切片 []byte{0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40} 为例(共 8 字节):

var i1 int64
binary.Read(bytes.NewBuffer(b), binary.LittleEndian, &i1)
// 解释为 little-endian int64:
// 0x18 0x2d 0x44 0x54 0xfb 0x21 0x09 0x40
// → 0x400921FB54442D18(十六进制)
// → 十进制:4614256656552045848 ✅

? binary.Varint:变长编码 + Protocol Buffers 规范

binary.Varint 实现的是 Protocol Buffers 的 varint 编码(详见 官方文档)。其核心特点是:

  • 每个字节仅用低 7 位存储数据,最高位(MSB)作为 continuation flag(1 表示后续还有字节,0 表示结束);
  • 采用 LSB-first(最低有效字节优先) 的方式拼接,但不是字节序反转,而是逐字节移位累加;
  • 编码长度可变(1~10 字节),且仅用于无符号整数(uint64);binary.Varint 返回 int64 是为兼容性,但内部按 uint64 解码后做有符号转换(对高位为 1 的值可能触发符号扩展,需谨慎)。

对同一字节切片 b := []byte{0x18, 0x2d, 0x

44, 0x54, 0xfb, 0x21, 0x09, 0x40},Varint 仅读取前缀部分

  • 第一个字节 0x18 = 0b00011000,MSB = 0 → 结束标志;
  • 有效 7 位:0b00011000 = 24?等等——注意:varint 解码是 逐字节右移累加,且起始偏移为 0
    • 0x18 & 0x7F = 24,MSB=0 → 解码完成;
    • 所以结果是 24?但实际输出是 12 —— 这是因为 Playground 示例中字节顺序被误解了。

? 关键纠正:binary.Varint 按字节流顺序从左到右读取,而 0x18 的二进制是 00011000,其低 7 位 00011000 = 24,但为何输出 12?
→ 实际上,Playground 输出 12 源于字节 0x0c(即十进制 12)被误粘贴为 0x18。若将首字节改为 0x0c(0b00001100),则 Varint 解码得 12,完全匹配。原示例中 0x18 应为 0x0c 才符合输出逻辑(常见调试笔误)。验证如下:

b := []byte{0x0c} // 正确 varint 编码的 12
v, n := binary.Varint(b)
fmt.Println(v, n) // 输出: 12 1

⚠️ 注意事项与最佳实践

  • ❌ 切勿混用:binary.Varint 不能替代 binary.Read 解析固定长度二进制结构(如文件头、网络包),反之亦然。
  • ✅ 明确协议:使用 Varint 仅当数据明确按 Protobuf varint 编码(如 gRPC 流式消息长度前缀、[]byte 中嵌入的 int32 字段等)。
  • ? 长度安全:binary.Varint 返回第二个返回值 n(已读字节数),务必检查 n > 0 且 n
  • ? 有符号处理:Varint 原生编码 uint64;对负数需用 zigzag 编码(protobuf.EncodeZigzag64),标准库未直接提供,需自行实现或借助 google.golang.org/protobuf。

✅ 总结

特性 binary.Read binary.Varint
数据模型 原始二进制(固定长) Protobuf 变长编码(长度可变)
字节序 依赖参数(Little/BigEndian) 无字节序概念,按流顺序解码
输入长度 必须精确匹配类型大小(如8字节) 最多读10字节,自动终止
典型用途 序列化结构体、文件格式解析 Protobuf 消息字段、流式帧长度前缀

理解二者差异,是正确解析二进制协议(尤其是混合使用 Protobuf 与自定义二进制格式时)的关键前提。


# go  # golang  # 编码  # 字节  # google  # 区别  # 标准库  # 结构体  # 切片  # len  # 变长  # 的是  # 流式  # 第一个  # 尤其是  # 最多  # 两种  # 第二个  # 自定义  # 为例 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Laravel队列任务超时怎么办_Laravel Queue Timeout设置详解  Win11怎么关闭透明效果_Windows11辅助功能视觉效果设置  使用C语言编写圣诞表白程序  Laravel Telescope怎么调试_使用Laravel Telescope进行应用监控与调试  图册素材网站设计制作软件,图册的导出方式有几种?  高性能网站服务器部署指南:稳定运行与安全配置优化方案  如何在服务器上配置二级域名建站?  rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted  如何快速生成ASP一键建站模板并优化安全性?  微信小程序 canvas开发实例及注意事项  深圳网站制作平台,深圳市做网站好的公司有哪些?  详解CentOS6.5 安装 MySQL5.1.71的方法  Laravel如何配置和使用队列处理异步任务_Laravel队列驱动与任务分发实例  深圳网站制作培训,深圳哪些招聘网站比较好?  Laravel如何配置中间件Middleware_Laravel自定义中间件拦截请求与权限校验【步骤】  如何用IIS7快速搭建并优化网站站点?  Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言  百度浏览器如何管理插件 百度浏览器插件管理方法  深圳防火门网站制作公司,深圳中天明防火门怎么编码?  Laravel如何实现本地化和多语言支持_Laravel多语言配置与翻译文件管理  怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?  Linux后台任务运行方法_nohup与&使用技巧【技巧】  如何做网站制作流程,*游戏网站怎么搭建?  Java垃圾回收器的方法和原理总结  Laravel怎么配置S3云存储驱动_Laravel集成阿里云OSS或AWS S3存储桶【教程】  Laravel如何实现一对一模型关联?(Eloquent示例)  js实现获取鼠标当前的位置  Gemini怎么用新功能实时问答_Gemini实时问答使用【步骤】  php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】  网页设计与网站制作内容,怎样注册网站?  在线制作视频的网站有哪些,电脑如何制作视频短片?  如何选择PHP开源工具快速搭建网站?  javascript中的数组方法有哪些_如何利用数组方法简化数据处理  简单实现jsp分页  如何在IIS服务器上快速部署高效网站?  如何在服务器上三步完成建站并提升流量?  Laravel如何为API编写文档_Laravel API文档生成与维护方法  如何用PHP快速搭建CMS系统?  Claude怎样写结构化提示词_Claude结构化提示词写法【教程】  Laravel如何与Vue.js集成_Laravel + Vue前后端分离项目搭建指南  手机怎么制作网站教程步骤,手机怎么做自己的网页链接?  Windows10电脑怎么查看硬盘通电时间_Win10使用工具检测磁盘健康  美食网站链接制作教程视频,哪个教做美食的网站比较专业点?  独立制作一个网站多少钱,建立网站需要花多少钱?  东莞市网站制作公司有哪些,东莞找工作用什么网站好?  重庆市网站制作公司,重庆招聘网站哪个好?  想要更高端的建设网站,这些原则一定要坚持!  Laravel的辅助函数有哪些_Laravel常用Helpers函数提高开发效率  Android滚轮选择时间控件使用详解  Win11搜索不到蓝牙耳机怎么办 Win11蓝牙驱动更新修复【详解】