go中无utf-16字符串,string恒为utf-8;[]uint16↔string需经编解码:unicode/utf16.decode仅做[]uint16→[]rune,不处理bom、字节序或代理对验证,适合已知格式场景;生产环境必须用golang.org/x/text/encoding/unicode,它支持bom自动识别、字节序协商及io流对接;cgo调windows api时须手动补零终止。

Go 语言里没有“UTF-16 字符串”这种东西——string 永远是 UTF-8 编码的字节序列。所谓“UTF-16 字符串转换”,本质是 []uint16 ↔ string(即 UTF-8)之间的编解码过程,性能差异主要来自底层实现路径和内存操作方式。
unicode/utf16.Decode + string() 是最轻量但不完整
标准库 unicode/utf16 的 Decode 函数只做 []uint16 → []rune,不处理字节序、BOM 或空终止。后续用 string(runes) 转成 UTF-8 字符串看似简单,但有硬伤:
- 它假设输入是纯 UTF-16LE 数据,若实际是 BE 或带 BOM,结果直接错乱
- 不验证 surrogate pair 合法性,非法序列(如
0xd800, 0xd800)会被静默转为\uFFFD,且无错误提示 - 两次内存分配:一次给
[]rune,一次给最终string底层字节,中间无复用
适合内部已知格式、追求极致速度的场景(比如解析固定结构的 Windows API 返回值),但绝不适用于读文件或网络流。
golang.org/x/text/encoding/unicode 是唯一生产级选择
真正可靠的 UTF-16 编解码必须用 golang.org/x/text/encoding/unicode,它封装了 BOM 自动识别、字节序协商、IO 流对接等全部逻辑。关键点:
-
unicode.UTF16(unicode.LittleEndian, unicode.UseBOM):写入时自动加\xff\xfe,读取时自动跳过前 2 字节 -
unicode.UTF16(unicode.BigEndian, unicode.ExpectBOM):强制要求 BE+BOM,缺则报encoding.ErrInvalidUTF16 - 与
io.Reader/io.Writer直接组合,避免中间[]byte拷贝(例如用transform.NewReader(r, enc.NewDecoder()))
相比手写逻辑,它多一次 io.Copy 级别的内存拷贝,但换来的是跨平台鲁棒性——Windows 记事本保存的 .txt、.csv,或 COM 接口返回的宽字符串,都能稳定处理。
Cgo 调用 Windows API 时的零字节陷阱
向 CreateFileW、SetWindowTextW 这类函数传 UTF-16 字符串,不是把 []uint16 直接转指针就行。Windows 要求 LPCWSTR 是 null-terminated 的 uint16 序列,末尾必须是 0x0000:
- 错误写法:
C.CString(string(utf16Bytes))→ 生成的是 UTF-8 字节流,完全无效 - 错误写法:
(*C.uint16_t)(unsafe.Pointer(&u16[0]))→ 若u16末尾没清零,API 可能读越界或截断 - 正确做法:分配
len(runes) + 1个uint16,最后一位显式设为0,再用C.CString或unsafe.Slice构造指针
这里性能瓶颈不在编码本身,而在内存对齐和手动补零——少一个 u16[len-1] = 0,调用就可能崩溃。
真正影响性能的从来不是 UTF-16/UTF-8 转换算法本身,而是你是否绕过了 BOM 处理、字节序协商、surrogate 验证这些必须步骤。用错包,跑得再快也是错的;用对包,慢那几纳秒在 I/O 场景里根本不可见。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











