
本文详解如何在 go 中正确、高效地将 unicode 字符串编码为大端序 utf-16 的十六进制字符串,澄清 ucs-2 与 utf-16 的常见误解,并提供可复用、无依赖的生产级实现。
本文详解如何在 go 中正确、高效地将 unicode 字符串编码为大端序 utf-16 的十六进制字符串,澄清 ucs-2 与 utf-16 的常见误解,并提供可复用、无依赖的生产级实现。
在 Go 中将字符串转换为 UTF-16(大端序)的十六进制表示,常被误称为“UCS-2 转换”。但需明确:UCS-2 已是过时术语,自 Unicode 2.0 起已被 UTF-16 取代。现代实现(包括 Python 的 'utf-16-be')实际使用的是 UTF-16 编码——它能正确处理 BMP 字符(如 é)及补充平面字符(如 emoji),而 UCS-2 无法表示后者。因此,本教程目标实为:将 Go 字符串按 UTF-16 Big-Endian 编码为连续小写十六进制字符串(无 BOM,无空格),与 Python 的 s.encode('utf-16-be').hex() 行为严格对齐。
以下是推荐的高效、清晰且符合标准的实现:
package main
import (
"fmt"
"strings"
"unicode/utf16"
)
// hexUTF16FromString 将字符串编码为 UTF-16BE 十六进制字符串(小写,无 BOM,无空格)
func hexUTF16FromString(s string) string {
runes := []rune(s)
utf16Units := utf16.Encode(runes)
// 使用 fmt.Sprintf("%04x") 格式化每个 uint16 为 4 位小写十六进制
var hexBuilder strings.Builder
for _, u := range utf16Units {
hexBuilder.WriteString(fmt.Sprintf("%04x", u))
}
return hexBuilder.String()
}
func main() {
str := "Bien joué"
fmt.Println(str)
fmt.Println(hexUTF16FromString(str)) // 输出: 004200690065006e0020006a006f007500e9
}✅ 关键改进说明:
- 避免字符串反复替换:原方案使用多次 strings.Replace 处理 fmt.Sprintf("%U") 输出(含 U+、括号、空格),时间复杂度高且易出错;新方案直接遍历 utf16.Encode 返回的 []uint16,逐个格式化,性能更优、逻辑更直观。
- 正确使用 utf16.Encode:该函数将 Unicode 码点([]rune)转换为 UTF-16 代码单元序列([]uint16),自动处理代理对(surrogate pairs),确保对 emoji(如 "??")等补充字符的支持。
- 严格 BE 序:utf16.Encode 输出即为大端序代码单元数组(每个 uint16 在内存中按平台字节序存储,但其数值本身即代表 BE 编码结果),无需额外字节序翻转;%04x 直接输出其十六进制表示,自然对应 0042(U+0042)、00e9(U+00E9)等。
⚠️ 注意事项:
- 此实现不添加 UTF-16 BOM(Byte Order Mark),与 Python 示例 encode('utf-16-be') 一致(-be 明确指定大端序,故省略 BOM)。若需 BOM,应在结果前手动添加 "feff"。
- 若输入含无效 Unicode(如孤立代理项),utf16.Encode 会将其替换为 0xfffd(Unicode 替换字符),行为符合标准。
- 不要使用 []byte 强转或 encoding/binary —— UTF-16 编码逻辑必须经由 unicode/utf16 包处理,以保证代理对正确性。
总结:摒弃对“UCS-2”的术语依赖,采用 unicode/utf16.Encode + fmt.Sprintf("%04x") 组合,是 Go 中实现标准兼容、高性能、可维护的 UTF-16HEX 转换的最佳实践。










