Go encoding/hex Encoder 怎样把二进制流连续写成十六进制文本
来源:17golang原创
时间:2026-10-24 13:30:39 217浏览 收藏
如果二进制数据不是一次性放在内存里,而是分几次从网络、文件或协议帧写出,encoding/hex.NewEncoder 更合适:它返回一个 io.Writer,每次收到源字节就转成小写十六进制字符并写给下游 Writer。这样可以把“读取分片”和“生成文本”连接起来,不需要先拼成一个大切片。
官方文档:https://pkg.go.dev/encoding/hex@go1.27.3#NewEncoder
hex.NewEncoder(dst)的职责是编码并转写,返回值按io.Writer使用。- 连续调用
Write会连续产生十六进制文本;每个源字节对应两个小写字符。 Write返回的n是已消费的源字节数,错误来自下游 Writer,要立即处理。
先把 Encoder 放在正确的边界上
NewEncoder 接收一个目标 io.Writer,例如 bytes.Buffer、文件或网络响应。它返回的仍然是 Writer,因此调用方只需要持续写入原始字节;编码器内部会把数据转成小写十六进制字符。这个 API 适合持续输出,不适合拿来反向解码,也不负责校验文件内容。
实现上,标准库会把输入分块编码后写给下游;官方源码中的内部缓冲区大小为 1024 个十六进制字符,调用方不应把这个实现细节当成接口承诺。真正需要关注的是:下游写失败后,后续写入会沿用这个错误,不能继续假装输出完整。

用一个小工具连续写入多个分片
下面的小程序把两个分片写进同一个编码器,再从缓冲区取回结果。第二次写入不会覆盖第一次结果,而是从上次结束的位置继续追加,因此输出可以对应连续的协议片段或日志块。
package main
import (
"bytes"
"fmt"
"encoding/hex"
)
func encodeChunks(chunks ...[]byte) (string, error) {
var dst bytes.Buffer
encoder := hex.NewEncoder(&dst)
for _, chunk := range chunks {
// 每个分片都写入同一个 Encoder,编码结果会自然连续。
if _, err := encoder.Write(chunk); err != nil {
// 下游 Writer 出错时立即返回,避免把不完整文本当成成功结果。
return "", err
}
}
return dst.String(), nil
}
func main() {
// 分片可以来自不同读取动作,这里只用两个字节切片演示边界。
text, err := encodeChunks([]byte{0x48, 0x69}, []byte{0x21})
if err != nil {
// 示例程序把错误直接打印出来;服务代码应按业务记录或返回。
panic(err)
}
fmt.Println(text) // 输出 486921
}
这里的 bytes.Buffer 只是内存中的下游 Writer;换成打开的文件或实现了 io.Writer 的网络对象,调用方式不变。由于编码结果长度是源数据的两倍,示例中的三个源字节最终得到六个十六进制字符。
分片写入时要看懂 n 和 err
很多代码只检查 err,却忽略了 n 的含义。对于 hex.NewEncoder 返回的 Writer,n 表示已经从本次输入切片中消费的源字节数量,不是已经写出的十六进制字符数量。若下游 Writer 只写出一部分并返回错误,编码器会把下游错误保留下来,调用方应停止继续发送。
func writeChunks(dst io.Writer, chunks [][]byte) error {
encoder := hex.NewEncoder(dst)
for _, chunk := range chunks {
// n 按源字节计算;不要用 n 直接当作十六进制字符串长度。
n, err := encoder.Write(chunk)
if err != nil {
// 记录当前分片和底层错误,调用方才能决定重试或终止。
return fmt.Errorf("写入十六进制分片失败,已消费 %d 个源字节: %w", n, err)
}
if n != len(chunk) {
// 正常的完整 Writer 应消费整个分片,短写无错误也不能静默忽略。
return fmt.Errorf("十六进制分片短写:%d/%d", n, len(chunk))
}
}
return nil
}
这个函数需要补充 fmt、io 和 encoding/hex 的导入。示例刻意把错误上下文放在调用处,方便把失败定位到具体分片;不要把一次失败后的编码器当成可恢复的全新 Writer 使用。

什么时候改用一次性编码 API
如果数据已经完整存在于一个切片中,hex.EncodeToString 更直接;如果要把结果追加到已有切片,可以使用 hex.AppendEncode。只有当输入会分段到达,或目标本身就是一个持续写入的 Writer 时,NewEncoder 才能体现它的边界优势。
| 场景 | 更合适的选择 | 原因 |
|---|---|---|
| 一次性字节切片 | hex.EncodeToString | 表达简单,直接得到字符串 |
| 追加到已有切片 | hex.AppendEncode | 由调用方管理目标切片 |
| 多个分片持续写出 | hex.NewEncoder | 保持 Writer 链,不必先合并输入 |
| 需要 hexdump 格式 | hex.Dumper | 输出带偏移和可打印区域的转储格式 |
常见问题
NewEncoder 输出大写十六进制吗?
不会。标准库文档说明它写出小写十六进制字符;如果协议要求大写,需要在边界处另行转换或选择能满足协议格式的编码方案。
每个分片之间会自动加空格或换行吗?
不会。多个 Write 的结果会直接连接。分隔符属于协议或日志格式的一部分,应由调用方明确写入,不能期待编码器自动插入。
写入失败后可以继续调用 Write 吗?
不建议。编码器会记住下游错误,后续写入不会恢复一个已经失败的输出链;应保留原始错误并结束这次编码任务。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
158 收藏
-
262 收藏
-
367 收藏
-
300 收藏
-
454 收藏
-
195 收藏
-
311 收藏
-
490 收藏
-
442 收藏
-
350 收藏
-
360 收藏
-
146 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习