因为Go语言中字符串底层是UTF-8编码的字节序列,直接用下标遍历会截断多字节Unicode字符,导致乱码;应使用range获取rune而非byte。

字符串遍历时为什么 for i := 0; i 会切错中文?
因为 len(s) 返回的是字节数,不是字符数;而中文、emoji 在 UTF-8 中占多个字节(如“你好”是 6 字节),用下标直接取 s[i] 拿到的只是某个字节,不是完整字符。
- 错误现象:
s := "你好"; fmt.Printf("%c", s[0])输出乱码或 ,因为s[0]是e4(UTF-8 第一字节),不是合法 Unicode 字符 - 正确做法:用
for _, r := range s—— Go 自动按 UTF-8 解码,每次迭代返回一个rune - 想转成可索引的字符数组?必须显式转换:
rs := []rune(s),之后rs[0]才是 '你'
截取前 N 个字符该用 []byte 还是 []rune?
要看你定义的“N 个字符”是指字节还是语义字符。日常业务中几乎总是后者——比如“取标题前 10 个字”,用户要的是 10 个汉字/字母,不是 10 个字节。
- 用
[]byte(s)[:10]:对英文安全,但对中文大概率截出半个字(如"你好世界"[0:4]得到"你好"的前两个字节e4 bd,无法打印) - 用
rs := []rune(s); rs[:min(10, len(rs))]:才真正截取前 10 个 Unicode 字符,再转回string即可 - 性能提醒:
[]rune(s)是 O(n) 拷贝 + 解码,高频短字符串可接受;超长文本(如日志)需权衡
string 转 []byte 和 []rune 的开销与用途差异
两者底层行为完全不同:[]byte(s) 是零拷贝(仅生成切片头),[]rune(s) 必须逐字节解码并分配新内存。
-
[]byte适用场景:HTTP body 读写、加密计算、文件 I/O —— 只关心字节流,不关心含义 -
[]rune适用场景:字符串反转、大小写转换、正则匹配、统计字符数(len([]rune(s))才是真实字数) - 常见坑:
len(s)≠len([]rune(s)),例如len("??") == 4(UTF-8 四字节),但len([]rune("??")) == 1
函数参数该选 byte 还是 rune?
看输入单位:如果函数处理的是单个 ASCII 符号(如分隔符 '|'、控制字符 '\n'),用 byte 更轻量;如果可能接收任意 Unicode 字符(如用户输入的首字母、表情符号过滤器),必须用 rune。
立即学习“go语言免费学习笔记(深入)”;
- 反例:
func startsWith(s string, c byte) bool—— 传入'中'会静默截断为低 8 位,结果不可靠 - 正例:
func startsWith(s string, c rune) bool,内部用[]rune(s)[0] == c或更优的strings.HasPrefix配合string(c) - 注意:
byte和rune不能直接比较,'中' == 20320是rune值,而'中' == 0x20320编译不过 —— 类型检查很严格
len、index、range 全都指向不同东西。


















