Go 中 Varint 编码与二进制字节读取的本质区别解析
技术百科
聖光之護
发布时间:2026-01-20
浏览: 次 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, 0x44, 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

→ 实际上,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 与自定义二进制格式时)的关键前提。
# 的是
# 尤其是
# 第一个
# google
# 最多
# 第二个
# 两种
# 为例
# 自定义
# go
# golang
# 编码
# 字节
# 区别
# 标准库
# 结构体
# 切片
# len
# 流式
# 变长
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- php怎么下载安装后设置错误日志_phpini l
- 如何使用正则表达式批量替换重复的“-”模式为固定字
- 如何在Golang中使用内置函数_Golangle
- Mac如何与安卓手机传文件_Mac和Android
- 如何使用Golang构建基础消息队列模拟_Gola
- Win11怎么关闭内容自适应亮度_Windows1
- Windows7如何安装系统镜像_Windows7
- Windows10系统怎么查看IP地址_Win10
- How to Properly Use NumPy
- Linux如何安装Golang环境_Linux下G
- Python随机数生成_random模块说明【指导
- Win11怎么关闭应用权限_Windows11相机
- Linux怎么禁止Root用户远程登录_Linux
- c++如何使用std::bind绑定函数参数_c+
- Win10怎样卸载iTunes_Win10卸载iT
- Python函数接口文档化_自动化说明【指导】
- 如何在Golang中捕获JSON序列化错误_Gol
- c++的static关键字有什么用 静态变量和静态
- Python多线程使用规范_线程安全解析【教程】
- Go 语言标准库为何不提供泛型 Contains
- Win11怎么设置右键刷新选项_Windows11
- Win10怎么创建桌面快捷方式 Win10为应用创
- Windows11如何设置专注助手_Windows
- Win11怎么开启游戏模式_Win11优化游戏帧数
- windows系统找不到无线网络怎么办_windo
- Python如何创建带属性的XML节点
- Win11怎么连接投影仪_Win11多显示器投屏设
- Windows电脑键盘突然失灵怎么办?(驱动与硬件
- windows如何测试网速_windows系统网络
- php文件怎么变mp4保存_php输出视频流保存为
- Windows10如何更改盘符名称_Win10重命
- 如何使用Golang实现Web表单数据绑定_自动映
- Win10系统怎么查看端口状态_Windows10
- c# 如何用c#实现一个支持优先级的任务队列
- Win11怎么打开旧版计算器_Win11恢复传统计
- Python性能剖析高级教程_cProfileLi
- Windows10系统怎么查看硬盘健康_Win10
- C++如何解析JSON数据?(nlohmann/j
- 如何在Golang中引入测试模块_Golang测试
- Win10怎样卸载自带Edge_Win10卸载Ed
- mac怎么退出id_MAC退出iCloud账号与A
- mac怎么看硬盘大小_MAC查看磁盘存储空间与文件
- Win11如何关闭游戏模式 Win11禁用Xbox
- 如何使用Golang实现容器安全扫描_Golang
- 如何使用Golang实现负载均衡_分发请求到多个服
- Win11 explorer.exe频繁崩溃_修复
- c++中explicit(bool)的用法 c++
- Python装饰器复用技巧_通用能力解析【教程】
- Windows笔记本无法进入睡眠模式怎么办?(电源
- Python数据挖掘核心算法实践_聚类分类与特征工

QQ客服