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

- 第一个字节 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蓝牙驱动更新修复【详解】

