Go中用s[start:end]切字符串是零分配的内置操作,但按字节索引,中文等Unicode字符需转[]rune按字符切;strings包无子串提取函数,安全切片需手动校验边界。

Go 语言中用切片语法获取子串最直接
Go 字符串底层是只读的字节序列([]byte),但支持类似切片的语法 s[start:end] 直接截取子串。这不是函数调用,而是语言内置操作,零分配、无拷贝(仅复制字符串头,含指针和长度),性能极高。
注意:下标按 字节索引,不是 rune 索引。中文、emoji 等 Unicode 字符占多个字节,直接用下标切可能截断 UTF-8 编码,导致乱码。
-
start和end必须在0 ≤ start ≤ end ≤ len(s)范围内,越界 panic - 省略
start默认为0;省略end默认为len(s) - 空字符串
""切片合法:""[0:0]返回""
package main
import "fmt"
func main() {
s := "你好world"
fmt.Println(s[0:6]) // 输出:好wor —— 错误!"你" 占 3 字节,s[0:6] 截到"好"中间
fmt.Println(s[3:9]) // 正确:从第 3 字节开始,取 6 字节 → "好world"
}
需要按字符(rune)切子串时必须先转 []rune
当逻辑上要“取前 3 个汉字”或“跳过前 2 个 emoji”,就必须按 Unicode 字符(rune)计数。Go 不允许对字符串直接做 rune 索引,需先转换为 []rune 切片,再操作。
⚠️ 转换有开销:会分配新底层数组,拷贝所有 rune。对长文本或高频调用需谨慎。
立即学习“go语言免费学习笔记(深入)”;
- 转换后下标即 rune 位置,
runes[i]是第 i 个字符 - 再用
string(runes[start:end])转回字符串 - 若原字符串含非法 UTF-8,
[]rune(s)会将每个非法字节转为0xFFFD()
package main
import "fmt"
func main() {
s := "??Go编程"
runes := []rune(s)
fmt.Println(string(runes[0:2])) // ??
fmt.Println(string(runes[2:4])) // Go
}
strings 包里没有“按索引切子串”的函数
strings 包专注语义操作(如 strings.Split、strings.Index、strings.Trim),不提供基于位置的子串提取函数。它没有 strings.Substr 或 strings.Slice 这类 API。
所以别搜 strings.substr——它不存在。强行封装也无必要,原生切片语法已足够高效简洁。
-
strings.Index/strings.LastIndex可帮你找起始/结束位置,再配合原生切片使用 -
strings.TrimSpace等是处理内容,不是定位切片 - 想“从第 n 个字符后取 m 个字符”,仍得走
[]rune转换路径
边界检查和安全切片建议
生产代码中,s[i:j] 的 i、j 常来自用户输入或计算结果,必须校验。Go 不做运行时范围裁剪,越界直接 panic。
推荐封装一个安全切片函数,尤其用于字节索引场景:
func safeSlice(s string, start, end int) string {
if start < 0 {
start = 0
}
if end > len(s) {
end = len(s)
}
if start > end {
return ""
}
return s[start:end]
}
若涉及 rune 级操作,校验逻辑更复杂:需先转 []rune,再比对 len(runes),否则仍可能 panic。这点容易被忽略——字节长度和 rune 长度完全不是一回事。










