小量拼接用+,编译器自动优化;循环拼接必须用strings.Builder并预估容量;切片越界因字节索引与rune混淆,安全操作需转[]rune或用utf8.DecodeRuneInString。

字符串拼接用 + 还是 strings.Builder?
小量拼接用 + 完全没问题,编译器会自动优化;但循环里反复拼接,+ 会引发多次内存分配和拷贝,性能断崖式下跌。
常见错误现象:for 循环中写 s += item,数据量一过千,耗时就明显上涨,pprof 显示大量 runtime.makeslice 调用。
- 单次或固定几处拼接:直接用
+,可读性好,无负担 - 循环拼接(尤其 >100 次):必须用
strings.Builder,先builder.Grow()预估容量更稳 - 格式化拼接优先考虑
fmt.Sprintf,但注意它比Builder慢 3–5 倍,别在 hot path 里用
var b strings.Builder
b.Grow(len(items) * 16) // 预估总长,避免多次扩容
for _, s := range items {
b.WriteString(s)
}
result := b.String()切片操作为什么有时 panic: index out of range?
Go 字符串底层是只读字节数组,s[i:j] 的索引是字节位置,不是 rune 个数。中文、emoji 等 Unicode 字符占多个字节,直接按“字符数”算下标必然越界。
使用场景:想取前 5 个汉字、截断超长日志、提取 URL path 片段——这些都得按 rune 处理,不能裸用字节切片。
立即学习“go语言免费学习笔记(深入)”;
-
s[0:5]取的是前 5 个字节,可能卡在某个中文中间,导致后续string()出乱码甚至 panic - 安全切片必须先转
[]rune:r := []rune(s); sub := string(r[0:min(5, len(r))]) - 性能影响:转
[]rune是 O(n) 拷贝,高频场景(如协议解析)建议用utf8.DecodeRuneInString迭代解码,不全加载
遍历字符串该用 for range 还是 for i := 0; i
必须用 for range。后者遍历的是字节,遇到多字节字符会把一个汉字拆成几个 0xe4 类似值,逻辑全错。
常见错误现象:用 s[i] 取字符做判断,结果中文变成负数、大小写判断失效、strings.Contains 返回 false —— 实际是拿字节当 rune 用了。
-
for i, r := range s中i是起始字节索引,r是当前 rune,这才是语义正确的遍历 - 如果真需要字节索引(比如解析二进制协议头),那就明确用
[]byte(s),并注释清楚“此处按 UTF-8 字节处理” - 不要混用:
len(s)返回字节数,utf8.RuneCountInString(s)才是字符数,两者不等价
字符串转数字用 strconv.Atoi 还是 strconv.ParseInt?
看需求是否要控制位宽和进制。strconv.Atoi 只是 ParseInt(s, 10, 0) 的封装,内部仍调 ParseInt,但隐藏了错误细节。
容易踩的坑:用 Atoi 解析带正负号的十六进制字符串(如 "-0xFF")直接返回 error,而 ParseInt(s, 0, 64) 支持自动识别 0x 前缀。
- 确定是十进制整数且范围在 int 内:用
Atoi简洁够用 - 需指定 bit 位宽(如解析配置项限制为 32 位):必须用
ParseInt(s, 10, 32),否则溢出时返回 0 和 error,不易排查 - 支持八进制/十六进制输入(如用户命令行参数):用
ParseInt(s, 0, 64),第二个参数传 0 表示自动识别前缀
转换失败时,error 不是 nil 就代表失败,别只检查返回值是否 0 —— 很多人在这里漏判 "abc" 这种非法输入。










