fmt.Sprintf不适合拼接大量字符串,因其内部需格式解析和反射,开销远高于+或strings.Builder;仅适用于带占位符的单次构造,如错误信息或调试日志。

fmt、
strings、
strconv、
slices 这四个包是日常编码中调用频率最高、最容易写错或误用的标准库工具。它们不涉及并发、网络或复杂抽象,但几乎每个 Go 文件都会 import 至少一个。
为什么 fmt.Sprintf 不该用来拼接大量字符串
fmt.Sprintf 内部会分配新字符串并做格式解析,哪怕只是
fmt.Sprintf("%s%s", a, b),开销也远高于直接拼接。
常见错误现象:循环里反复用
fmt.Sprintf 构建日志或 SQL 片段,CPU 占用突增、GC 频繁。
使用场景:仅适用于「带格式占位符的单次构造」,比如
fmt.Sprintf("user_id=%d, status=%s", id, status)。
性能影响:比
strings.Builder 慢 3–5 倍(实测 10K 次拼接);比
+ 运算符慢 2 倍以上(小字符串)。
可这样做:
- 纯拼接优先用
+(短字符串,≤3 段)或 strings.Builder(长/多段/循环内)
- 需要格式化时才用
Sprintf,且避免在 hot path 中调用
- 别用
fmt.Sprintf("%v", x) 替代 fmt.Sprint(x)——前者多一层解析
strings.Split 的空字符串陷阱
strings.Split("", ",") 返回
[]string{""},不是
[]string{};
strings.Split("a,,b", ",") 返回
[]string{"a", "", "b"}。
这不是 bug,是文档明确定义的行为:按分隔符切分,**保留所有空段**。
容易踩的坑:
- 用
len(strings.Split(s, ",")) 判断 CSV 字段数 → 实际字段数可能少于结果长度(因空段)
- 未过滤空字符串就直接取
parts[0] → panic if s == ""
- 误以为
Split 等价于 Python 的 str.split()(后者默认忽略空段)
正确做法:
- 需要“非空分割”时,先
strings.TrimSpace,再 Split,最后用 slices.DeleteFunc 或手动遍历过滤
- 取首段前加
if len(parts) > 0 判断
- 处理 CSV 请用
encoding/csv 包,而非手撕 Split
strconv.Atoi 和 strconv.ParseInt 怎么选
strconv.Atoi 是
ParseInt(s, 10, 0) 的快捷封装,返回
int 类型;
ParseInt 更底层,支持任意进制和位宽。
关键差异:
-
Atoi 无法指定 bitSize → 在 32 位系统上返回 int32,64 位上返回 int64,行为不跨平台
-
ParseInt 第三个参数必须是 0/8/16/32/64 → 显式控制目标类型,适合序列化/协议解析
-
Atoi 对负号处理更宽松(如 "-123" OK),但 ParseInt(" -123", 10, 64) 会失败(开头空格不被跳过)
建议:
- 读配置、命令行参数等“人输”的整数,用
Atoi(简洁、容错高)
- 解析二进制协议、JSON 数字字段、文件头数据,用
ParseInt(s, 10, 64)(类型确定、无歧义)
- 永远检查第二个返回值
err —— nil 才代表转换成功
Go 1.21+ 的 slices 包替代了哪些手写逻辑
slices 是泛型工具包,覆盖了大多数切片操作需求,但很多人仍写 for 循环或 copy/paste 工具函数。
典型被替代场景:
-
for i := range s { if s[i] == x { return true } } → slices.Contains(s, x)
-
sort.Ints(s); sort.Reverse(sort.IntSlice(s)) → slices.SortFunc(s, func(a, b int) bool { return a > b })
-
res := make([]T, 0, len(s)); for _, v := range s { if f(v) { res = append(res, v) } } → slices.DeleteFunc(s, func(v T) bool { return !f(v) })(注意是“删除不满足条件的”,语义需反转)
注意点:
-
slices 所有函数都要求切片元素类型可比较(== 有效),不支持 map/slice/func 等不可比较类型
-
slices.Compact 只去相邻重复项,不是去重全集;要全去重得自己写或用 map 辅助
- 它不提供“查找索引”接口(如
Index),需用 slices.IndexFunc 配合闭包
slices 和
strings 的设计哲学一致:只做最常用、最易出错的那几件事,不做通用抽象。真正难的不是“怎么写”,而是“什么时候不该写”——比如看到
for 循环遍历切片找元素,第一反应不该是敲代码,而是翻翻
slices 文档。