hex.decodestring仅支持合法十六进制字符串与[]byte互转,要求输入偶数长度、纯0-9a-fa-f字符;空格、换行、“0x”前缀、奇数长度或非法字符均触发invalid byte错误,需提前trim、去前缀、校验长度。

hex.EncodeToString 和 hex.DecodeString 不是字符串通用转换器,它们只在 []byte 和**合法十六进制字符串**之间互转。传错类型、忽略清洗、混用场景,90% 的报错都源于此。
为什么 hex.DecodeString 总报 encoding/hex: invalid byte
这不是函数坏了,是输入没清理干净。它只认严格格式:偶数长度、每个字符必须是 0-9a-fA-F,其余一律拒收。
- 空格、换行、制表符(
' '、'\n')→ 报encoding/hex: invalid byte: U+0020 ' ' - 奇数长度(如
"a"、"ff1")→ Go 1.22+ 返回odd length hex string错误 - 前缀
"0x"或"0X"→'x'被当非法字符,错误信息里会明确写出U+0078 'x' - 全角字符、中文标点、字母
'g'或'Z'→ 全部触发invalid byte - 调试时别信
fmt.Println(s),用fmt.Printf("%q", s)才能看到真实字节和不可见符
解码前必须做的三步清洗
不能跳过,否则错误无法避免:
- 先用
strings.TrimSpace(s)去首尾空白(HTTP header、JSON 字段、用户粘贴内容常含) - 再用
strings.TrimPrefix(strings.TrimPrefix(s, "0x"), "0X")剔除大小写前缀 - 最后检查长度:
if len(s)%2 != 0,奇数长度直接返回错误,不要传给hex.DecodeString - 若来源不可控(比如日志里复制的 dump),可加一步白名单过滤:
strings.Map(func(r rune) rune { if ('0' ,但注意这会丢弃所有非 hex 字符,语义是否允许得看场景
hex.EncodeToString 传 string 就错?
Go 的 string 是 UTF-8 字节序列,不是“字符数组”。hex.EncodeToString 只接受 []byte;若你传入 string 类型,必须显式转成 []byte(s),否则编的是它的 UTF-8 字节,不是你预期的原始数据。
- 如果原始数据是
[]byte(如crypto/sha256.Sum256结果):直接hex.EncodeToString(data) - 如果原始是
string,且你想编码它的 UTF-8 字节(绝大多数场景):先转[]byte(s),再hex.EncodeToString([]byte(s)) - 如果原始是
string,但内容本身就是十六进制(如"a1b2"):别用这个函数——那是解码场景,该用hex.DecodeString - 结果永远是小写;要大写得额外调用
strings.ToUpper(hex.EncodeToString(data))
高频调用时性能掉得厉害,怎么办?
hex.EncodeToString 每次都会 new 一个 []byte,在 hot path(如日志、序列化循环)反复调用小数据(16 字节 UUID、32 字节哈希),GC 压力明显。
- 对固定长度数据(如 SHA-256 输出 32 字节):复用缓冲区,
dst := make([]byte, hex.EncodedLen(32)),然后hex.Encode(dst, src) -
hex.EncodedLen(n)返回的是编码后字节数(2×n),不是字符串长度——它和len(string(dst))相等,但类型不同 - 临时调试打印?
fmt.Printf("%x", data)更轻量,不分配内存,也不依赖encoding/hex - 切勿在 hot path 使用
hex.Dump——它带偏移和 ASCII 预览,开销比EncodeToString高,仅限调试
真正容易被忽略的点是:错误不是来自函数本身,而是输入是否“合法”;而“合法”的定义非常窄——偶数长度 + 全 hex 字符 + 无空白 + 无前缀。任何一步漏掉,DecodeString 就立刻失败,没有容错余地。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











