小量拼接用+,大量用strings.Builder,带格式才用fmt.Sprintf;注意len()返回字节数而非字符数,Unicode处理需用utf8.RuneCountInString。

用 + 拼接字符串最简单,但要注意性能
Go 中直接用 + 连接两个 string 是合法且直观的,比如 "hello" + " " + "world"。它适合拼接少量、固定数量的字符串。
但每次 + 都会创建新字符串(因为 Go 字符串是不可变的),若在循环中反复拼接(比如构建日志或 HTML 片段),会产生大量临时对象,GC 压力明显上升。
- 小规模拼接(≤ 5 个片段):放心用
+ - 循环内拼接或不确定长度:改用
strings.Builder - 不要用
fmt.Sprintf替代拼接——它带格式解析开销,纯连接场景下比+还慢
大量拼接优先选 strings.Builder,不是 bytes.Buffer
strings.Builder 是 Go 1.10+ 专为字符串拼接优化的类型,底层复用 []byte 切片,零拷贝扩容,性能接近手动预分配切片。
它比 bytes.Buffer 更轻量:不支持读操作、无锁设计、String() 方法不额外拷贝(只要没调过 Reset() 或写入非字符串数据)。
立即学习“go语言免费学习笔记(深入)”;
- 初始化时可预估长度:
var b strings.Builder; b.Grow(1024)减少扩容次数 - 写入用
b.WriteString(s),不是b.Write([]byte(s))—— 后者多一次转换 - 避免混用
b.Write()和b.WriteString(),否则String()可能触发意外拷贝
fmt.Sprintf 适合带格式的拼接,别当通用连接器用
fmt.Sprintf 的作用是「格式化」,不是「拼接」。当你需要插入变量、对齐、进制转换(如 %x)、或类型自动转字符串时,它不可替代。
但如果你只是把几个字符串按顺序连起来,比如 fmt.Sprintf("%s%s%s", a, b, c),这比 a + b + c 慢 3–5 倍,还隐含格式解析逻辑。
- 真正需要格式控制时才用:
fmt.Sprintf("user_id=%d, name=%q", id, name) - 纯连接场景下,
fmt.Sprintf是反模式 - 注意
fmt.Sprint/fmt.Sprintln无格式符,但仍有反射开销,也不如+或Builder
拼接含中文或 emoji 时,别误用 len() 计算字节数
Go 的 string 底层是 UTF-8 字节序列,len(s) 返回字节数,不是字符数。拼接本身不受影响,但如果你基于 len() 做截断、分片或预分配,容易出错。
例如 len("你好") == 6(每个汉字占 3 字节),len("??") == 12(emoji 组合序列更长)。这时候用 utf8.RuneCountInString(s) 才能得到真实字符数。
- 拼接操作本身无需关心 Unicode ——
+和Builder都按字节处理,结果正确 - 只有涉及「按字符位置切分」「限制显示宽度」「计算显示长度」时,才需切换到
rune层面 - 别用
[]rune(s)[i]频繁索引长字符串 —— 转换开销大,优先考虑strings.IndexRune等原生函数
+,大量用 strings.Builder,带格式才碰 fmt.Sprintf;而 Unicode 处理的坑,往往不在拼接那行代码里,而在你假设 len() 等于“长度”的地方。










