Go中字符串切片s[2:5]按字节操作易致乱码或panic,因string是UTF-8字节序列,中文、emoji占多字节;安全截取需先转[]rune再索引。

Go 里没有 substring,直接写 s[2:5] 是按字节切——含中文、emoji 的字符串大概率乱码或 panic。
为什么 s[2:5] 会出问题
Go 的 string 是 UTF-8 字节序列,len(s) 返回字节数,不是字符数。一个汉字占 3 字节,emoji 可能占 4 字节。用字节索引切,很容易切在中间,产生非法 UTF-8 序列。
-
"你好"的len是 6,但只有 2 个字符;s[1:4]会截出半个“你”,打印出来是好 - 索引超出字节长度直接 panic:
panic: runtime error: slice bounds out of range -
nil *string或带 BOM(\uFEFF)的输入不处理,上线就崩
按字符安全截取:必须转 []rune
这是最通用、最可控的方式,适用于“取前 N 个字符”“跳过前 M 个再取 K 个”这类需求。
- 先转:
rs := []rune(s)—— 这步不能跳,否则所有后续索引都不可靠 - 防越界:
start := min(2, len(rs))、end := min(start+4, len(rs)) - 再切再转:
string(rs[start:end]) - 空字符串统一用
if s == ""判断;若接收的是*string,得先if s == nil || *s == ""
语义化截取:别硬算 rune 索引
如果目标是“取冒号前的内容”“第二段下划线分隔的字段”,用 strings 包更稳、更快、更易读。
立即学习“go语言免费学习笔记(深入)”;
- 找位置后切:
pos := strings.IndexRune(s, ':'); if pos != -1 { s[:pos] } - 按分隔符拆:
parts := strings.SplitN(s, "_", 3); if len(parts) > 1 { parts[1] } - 去固定前后缀:
strings.TrimPrefix(s, "prefix:")比strings.Index+ 切片拼接干净得多 - 避免反复调用
strings.Index后手动切片,性能差且边界难控
提取引号内内容:正则要选对模式
用正则不是不行,但 "(.*)"(贪婪)会跨多对引号;"(.+?)"(惰性)仍可能匹配换行或未闭合引号。推荐明确排除引号本身。
- 首选:
re := regexp.MustCompile(`"([^"]*)"`)—— 安全、高效、无需担心转义干扰 - 提取时用
re.FindAllStringSubmatch([]byte(s), -1),再对每个匹配取[1:-1]去引号 - 若需进一步按空格分词,对提取结果调用
strings.Split(sub, " ")即可 - 注意:正则有开销,纯结构化文本优先考虑
strings手动解析
最常被忽略的其实是 []rune 转换那一步——本地跑英文测试全过,一上生产遇到用户昵称里的 ? 或 “?” 就 panic。别图快跳过它。


















