登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

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 个十六进制字符,调用方不应把这个实现细节当成接口承诺。真正需要关注的是:下游写失败后,后续写入会沿用这个错误,不能继续假装输出完整。

Go encoding/hex Encoder 将分片字节、编码器和下游 Writer 连接成连续十六进制文本的结构说明图
图1:结构说明图,展示分片字节进入 hex Encoder 后写入下游 Writer 的静态关系,不是运行截图。

用一个小工具连续写入多个分片

下面的小程序把两个分片写进同一个编码器,再从缓冲区取回结果。第二次写入不会覆盖第一次结果,而是从上次结束的位置继续追加,因此输出可以对应连续的协议片段或日志块。

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 使用。

Go hex Encoder Write 返回源字节计数并传播下游错误的边界说明图
图2:边界说明图,展示 Write 的源字节计数、下游错误和完整分片之间的静态关系,不是运行证据。

什么时候改用一次性编码 API

如果数据已经完整存在于一个切片中,hex.EncodeToString 更直接;如果要把结果追加到已有切片,可以使用 hex.AppendEncode。只有当输入会分段到达,或目标本身就是一个持续写入的 Writer 时,NewEncoder 才能体现它的边界优势。

场景更合适的选择原因
一次性字节切片hex.EncodeToString表达简单,直接得到字符串
追加到已有切片hex.AppendEncode由调用方管理目标切片
多个分片持续写出hex.NewEncoder保持 Writer 链,不必先合并输入
需要 hexdump 格式hex.Dumper输出带偏移和可打印区域的转储格式

常见问题

NewEncoder 输出大写十六进制吗?

不会。标准库文档说明它写出小写十六进制字符;如果协议要求大写,需要在边界处另行转换或选择能满足协议格式的编码方案。

每个分片之间会自动加空格或换行吗?

不会。多个 Write 的结果会直接连接。分隔符属于协议或日志格式的一部分,应由调用方明确写入,不能期待编码器自动插入。

写入失败后可以继续调用 Write 吗?

不建议。编码器会记住下游错误,后续写入不会恢复一个已经失败的输出链;应保留原始错误并结束这次编码任务。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>