for range 是唯一安全的 unicode 字符串遍历方式,因 go 字符串底层是 utf-8 字节序列,str[i] 返回 byte 而非 rune,直接索引或切片会截断多字节字符导致乱码;需用 for range 获取 rune 或显式转 []rune 进行字符级操作。

Go 里处理 Unicode 字符串,for range 是唯一安全的遍历方式;直接用 str[i] 或 str[:n] 必然切乱中文、emoji 和带变音符号的字符。
为什么 str[i] 会出错
Go 的 string 是 UTF-8 字节序列,str[i] 拿到的是单个 byte,不是字符。一个中文占 3 字节,str[0] 可能只是「北」的第一个字节 0xe5,单独打印就是乱码或 。
-
len("北京")返回 6(字节数),不是 2(字符数) -
fmt.Printf("%c", str[0])对中文输出,因为%c期待完整rune - 写
if str[i] == '中'永远不成立——'中'是rune,str[i]是byte
for range 遍历字符串的正确用法
它自动按 rune 解码,每次迭代返回字节起始位置 i 和字符值 r(类型为 rune)。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
r可直接和'中'、'?'比较,无需转换 -
unicode.IsLetter(r)这类函数必须传rune,不能传str[i] -
i是字节偏移,不是“第几个字符”——「你好」中「好」的i是 3,不是 1 - 不需要
[]rune(str)就能安全遍历,省内存、无分配开销
for i, r := range "Hello世界" {
fmt.Printf("字节位置 %d: %c (U+%04X)\n", i, r, r)
}
需要按“第 N 个字符”索引时才转 []rune
只有当你需要随机访问、切片、修改顺序(比如取第 5 个汉字、反转、去重相邻字符),才必须显式转成 []rune。
-
rs := []rune(s)后,rs[4]才是第五个 Unicode 字符 -
s[4]在中文字符串里大概率是某个汉字的中间字节,毫无意义 -
string(rs[2:5])得到真正的“第 3–5 个字符”,而s[2:5]极可能截断 UTF-8 - 转换有开销:内存占用约翻 4 倍(
rune是int32),MB 级日志里反复转要警惕 GC
切片和修改字符串的惯用写法
字符串不可变,所有修改都得走 []rune 中转。切片也一样——别信 s[:10] 能截前 10 个字符。
- 截前 10 个字符:
rs := []rune(s); string(rs[:min(10, len(rs))]) - 替换第 3 个字符:
rs := []rune(s); rs[2] = 'X'; s = string(rs) - 过滤 emoji:
rs := []rune(s); filtered := make([]rune, 0, len(rs)); for _, r := range rs { if !isEmoji(r) { filtered = append(filtered, r) } } - 别用
[]byte(s)做 Unicode 处理——它只对纯 ASCII 安全
真正难的不是语法,而是记住:Go 的 string 没有“字符索引”这回事,len、[:]、[i] 全是字节操作。只要涉及中文、日文、阿拉伯文或 emoji,就默认它们失效——除非你先过一道 for range 或 []rune。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










