因为Go中string是UTF-8字节序列,直接按字节倒序会截断多字节Unicode字符导致乱码;正确做法是先转为[]rune,再用双指针原地交换,最后转回string。

为什么 string 不能直接用 for 循环倒序拼接?
因为 Go 的 string 是 UTF-8 编码的字节序列,直接按字节索引会切开多字节的 Unicode 字符(比如中文、emoji),导致乱码。你看到的不是反转,是字节层面的“错位截断”。
- 错误写法:
for i := len(s)-1; i >= 0; i-- { result += string(s[i]) }—— 对"你好"会输出一堆 - 正确前提:必须先转成
[]rune,让每个元素对应一个逻辑字符(Unicode code point) -
rune是 int32 别名,[]rune(s)会做 UTF-8 解码,安全拆分字符
用 for + []rune 反转的最简实现
这是最常用、可读性好、无额外依赖的方式,适合绝大多数场景。
- 先转
[]rune,再双指针原地交换,最后转回string - 避免字符串拼接(
+=)带来的多次内存分配 - 示例:
func reverseString(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}注意:string(r) 是安全的,Go 运行时会把 []rune 重新编码为 UTF-8 字节流。
什么情况下不该用 []rune?
当你处理的是纯 ASCII 或已知单字节字符集(如 HTTP header 名、base64 片段),且对性能极度敏感时,[]rune 转换有额外开销(解码 + 分配新切片)。
立即学习“go语言免费学习笔记(深入)”;
- ASCII 场景可直接操作字节:
[]byte(s),反转后转string - 但一旦输入含非 ASCII 字符(哪怕只是个
é),就立刻退化为乱码 - 没有运行时检查 —— Go 不会在
string转[]byte时报错,出问题才暴露 - 除非你 100% 控制输入,否则别省这个转换
strings.Builder 能不能用来反转?
能,但没必要。它只解决拼接效率问题,不解决字符边界问题 —— 你依然得先转 []rune,再从后往前 WriteRune。
- 比
+=高效,但比双指针原地交换多一次遍历和内存写入 - 代码更长,可读性没提升,还引入了额外类型
- 除非你在同一段逻辑里 already 在用
strings.Builder做其他拼接,否则纯属画蛇添足
真正容易被忽略的是:rune 数组长度 ≠ 字节数,len([]rune(s)) 可能远小于 len(s);而所有基于字节长度的假设(比如预分配缓冲区、切片索引计算)在这里都会失效。










