该用 strings.contains 判断存在性,strings.index 用于获取位置;前者更直观安全,后者需防 -1 越界;二者对 utf-8 字符串行为一致,但都不支持正则或忽略大小写。

什么时候该用 strings.Contains 而不是 strings.Index
strings.Contains 只关心“有没有”,返回 bool;strings.Index 还要算“在哪儿”,返回 int(位置)或 -1。如果你只需要判断存在性,用 strings.Contains 更直观、更安全——它不会因误判 -1 而引发逻辑错误。
常见误用场景:写 if strings.Index(s, "abc") != -1 判断包含,其实和 strings.Contains(s, "abc") 等价,但多了一次整数比较,还容易漏掉 -1 的边界检查。
- 纯存在性校验(如配置开关、日志关键词过滤)→ 优先
strings.Contains - 后续需要子串位置(比如截取、替换、高亮)→ 必须用
strings.Index - 性能差异极小(两者底层都走同一段字节扫描逻辑),别为“快几纳秒”强行统一用某一个
strings.Index 返回值为 -1 的含义和典型陷阱
strings.Index 在未找到时返回 -1,这不是错误码,而是约定值。很多人把它当成 Go 的 error 处理模式,结果写出类似 if err != nil 的条件判断,这是错的——-1 是合法返回值,不是 error。
典型坑点:
- 直接对
strings.Index返回值做切片操作:s[index:]→ panic:索引越界(当index == -1) - 用
index > 0判断存在 → 错!首字符匹配时index == 0,会被误判为“不存在” - 与
len(s)混淆:有人写index >= len(s)来判断失败,其实永远不成立,因为strings.Index只返回-1或[0, len(s)-len(substr)]范围内的值
正确写法始终是:if index != -1 { ... }
中文、emoji 等 Unicode 字符下,strings.Index 和 strings.Contains 行为一致吗
一致。Go 的 strings 包操作的是 UTF-8 编码的字节序列,不是 rune。这意味着:
- 对 ASCII 字符(如
"hello"中找"ll"),两者行为和预期完全一致 - 对中文(如
"你好")或 emoji(如"?"),只要子串本身是完整有效的 UTF-8 序列,查找仍准确——因为strings.Index扫描的是字节,而 Go 字符串字面量保证 UTF-8 合法性 - 但如果你手动拼接字节(比如用
[]byte截断某个中文字符的前两个字节),再转成string传给这两个函数,结果不可靠:可能找不到,也可能匹配到非法碎片
不需要额外转换 rune 切片,除非你要按字符(而非字节)定位——那得用 strings.IndexRune 或遍历 for i, r := range s。
想提前退出或处理多个匹配?别硬套 strings.Contains
strings.Contains 只能回答一次“有没有”,无法告诉你第几次出现、或者跳过前 N 次。如果需求超出单次布尔判断,strings.Index 就必须出场,而且往往要配合循环。
例如:找出所有匹配起始位置
positions := []int{}
i := 0
for {
index := strings.Index(s[i:], substr)
if index == -1 {
break
}
positions = append(positions, i+index)
i += index + 1 // 或 + len(substr) 跳过已匹配部分
}
注意这里 i += index + 1 是为了支持重叠匹配(如 "aaaa" 中找 "aa");若不需重叠,用 i += index + len(substr) 更高效。
别试图用 strings.Contains 加计数模拟——它不提供偏移控制,也没法复位搜索起点。
真正容易被忽略的点是:strings.Contains 和 strings.Index 都不支持正则、大小写忽略或模糊匹配。有这类需求时,别硬改参数,该换 regexp 或第三方库就换。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











