Как в Go проверить, содержит ли строка подстроку? От новичка до профи
Вы когда-нибудь писали на Go и вдруг замирали перед простым вопросом: «А содержит ли эта строка нужную мне часть?» Если да — вы не одиноки. На Stack Overflow этот вопрос набрал тысячи просмотров, и не зря. В Go есть несколько элегантных способов решить эту задачу, но какой выбрать? Давайте разберемся раз и навсегда.
В этой статье мы не просто покажем вам функцию strings.Contains. Мы научим вас думать как Go-разработчик: выбирать оптимальный инструмент для каждой ситуации, избегать подводных камней и писать код, который будет работать быстро и предсказуемо.
Содержание
- strings.Contains — ваш первый друг
- strings.Index — когда нужно больше, чем да/нет
- ContainsAny и ContainsRune — тонкая настройка
- Производительность: что выбрать?
- Крайние случаи и подводные камни
- Итоги: что запомнить
1. strings.Contains — ваш первый друг
Начнем с самого простого и популярного способа. Функция strings.Contains из стандартной библиотеки Go — это то, что вам нужно в 80% случаев. Она возвращает true, если строка содержит указанную подстроку, и false в противном случае.
Вот как это выглядит в реальном коде:
package main
import (
"fmt"
"strings"
)
func main() {
str := "Привет, мир Go!"
substr := "мир"
if strings.Contains(str, substr) {
fmt.Println("Строка содержит подстроку:", substr)
} else {
fmt.Println("Подстрока не найдена")
}
}
Вывод:
Строка содержит подстроку: мир
Просто, правда? Функция чувствительна к регистру: strings.Contains("Go", "go") вернет false. Если вам нужно игнорировать регистр, придется использовать strings.ToLower или strings.EqualFold.
Пример с игнорированием регистра
str := "Golang is awesome"
substr := "golang"
if strings.Contains(strings.ToLower(str), strings.ToLower(substr)) {
fmt.Println("Найдено (без учета регистра)!")
}
Когда использовать: когда вам нужен простой бинарный ответ — «есть» или «нет». Без лишних деталей.
2. strings.Index — когда нужно знать позицию
Иногда мало знать, что подстрока есть. Хочется понять, где именно она находится. Например, вы парсите лог и хотите вырезать часть строки после определенного маркера. Тут на помощь приходит strings.Index.
Эта функция возвращает индекс первого вхождения подстроки (начиная с 0) или -1, если подстрока не найдена.
str := "Ошибка: файл не найден в /var/log"
substr := "файл"
pos := strings.Index(str, substr)
if pos != -1 {
fmt.Printf("Подстрока найдена на позиции %d\n", pos)
fmt.Printf("Контекст: %s\n", str[pos:])
} else {
fmt.Println("Подстрока не найдена")
}
Вывод:
Подстрока найдена на позиции 7
Контекст: файл не найден в /var/log
Есть и другие вариации:
strings.LastIndex— ищет последнее вхождениеstrings.IndexAny— ищет первое вхождение любого символа из набораstrings.IndexRune— ищет конкретный Unicode-символ
// Пример с LastIndex
str := "a-b-c-d"
lastDash := strings.LastIndex(str, "-")
fmt.Println("Последнее тире на позиции:", lastDash) // 5
// Пример с IndexAny
str2 := "hello world"
pos := strings.IndexAny(str2, "aeiou")
fmt.Println("Первая гласная на позиции:", pos) // 1 (буква 'e')
Когда использовать: когда вам нужно не только проверить наличие, но и получить позицию для дальнейшей обработки.
3. ContainsAny и ContainsRune — тонкая настройка
Go предлагает еще несколько полезных функций для специфических случаев.
strings.ContainsAny
Проверяет, содержит ли строка любой из перечисленных символов. Обратите внимание: это не подстроки, а отдельные символы (руны).
str := "Gopher!"
hasVowel := strings.ContainsAny(str, "aeiouy")
fmt.Println("Содержит гласную?", hasVowel) // true
hasDigit := strings.ContainsAny(str, "0123456789")
fmt.Println("Содержит цифру?", hasDigit) // false
strings.ContainsRune
Проверяет наличие конкретного Unicode-символа (руны). Это эффективнее, чем искать односимвольную строку.
str := "Привет, мир!"
runeToFind := 'м'
if strings.ContainsRune(str, runeToFind) {
fmt.Println("Руна найдена!")
}
Когда использовать: ContainsAny — для быстрой проверки на наличие любого символа из набора (например, валидация пароля). ContainsRune — когда работаете с Unicode и знаете точный символ.
4. Производительность: что выбрать?
Go славится своей производительностью, и строковые операции здесь не исключение. Но разные функции имеют разную сложность. Давайте сравним:
| Функция | Сложность | Лучший сценарий |
|---|---|---|
strings.Contains |
O(n + m) | Простая проверка на вхождение |
strings.Index |
O(n + m) | Нужна позиция подстроки |
strings.ContainsAny |
O(n * k) | Проверка на любой символ из набора |
strings.ContainsRune |
O(n) | Поиск одного символа |
Примечание: n — длина строки, m — длина подстроки, k — количество символов в наборе.
На практике для большинства задач разница незаметна. Но если вы обрабатываете миллионы строк или работаете с очень длинными текстами (логи, ген