Avatar ·

Как проверить, содержит ли строка подстроку в Go?

Привет! Я только начал изучать Go и столкнулся с простой, на первый взгляд, задачей: нужно проверить, содержит ли строка определённую подстроку. В других языках, например в Python, я бы написал `if "sub" in "string":`, но в Go такой оператор не работает. Пробовал искать в документации, но пока не разобрался. Мне нужно понять, как правильно и эффективно решить эту задачу. Вот пример кода, который я пытаюсь написать: ```go package main import "fmt" func main() { str := "Hello, World!" substr := "World" // Как здесь проверить, что str содержит substr? // if str содержит substr { // fmt.Println("Подстрока найдена!") // } } ``` Я знаю, что в Go есть пакет `strings`, но не уверен, какая функция из него подходит. Видел упоминания `Contains`, `Index` и `Count`, но не понимаю, в чём разница и какую лучше использовать. Также интересует: 1. Как быть с учётом регистра? Нужно ли как-то специально обрабатывать регистр букв? 2. Есть ли разница в производительности между разными подходами? 3. Что делать, если нужно проверить несколько подстрок или выполнить более сложный поиск? Буду благодарен за подробное объяснение с примерами кода! Заранее спасибо за помощь. <<>>

Использование strings.Contains — самый простой и правильный способ

В Go для проверки, содержит ли строка подстроку, используется функция strings.Contains из стандартного пакета strings. Она возвращает true, если подстрока найдена, и false в противном случае. Это самый простой, читаемый и рекомендуемый подход.

Вот как исправить ваш пример:

package main

import (
    "fmt"
    "strings"
)

func main() {
    str := "Hello, World!"
    substr := "World"
    
    if strings.Contains(str, substr) {
        fmt.Println("Подстрока найдена!")
    } else {
        fmt.Println("Подстрока не найдена")
    }
}
// Вывод: Подстрока найдена!

Разница между Contains, Index и Count

Все эти функции находятся в пакете strings, но имеют разные цели:

  • strings.Contains(str, substr) — возвращает bool. Оптимален для простой проверки наличия подстроки.
  • strings.Index(str, substr) — возвращает int (индекс первого вхождения) или -1, если подстрока не найдена. Используйте, если нужно узнать позицию подстроки.
  • strings.Count(str, substr) — возвращает int (количество непересекающихся вхождений). Используйте, если нужно подсчитать, сколько раз встречается подстрока.

Пример с Index:

str := "Hello, World! World!"
substr := "World"

index := strings.Index(str, substr)
if index != -1 {
    fmt.Printf("Подстрока найдена на позиции %d\n", index)
} else {
    fmt.Println("Подстрока не найдена")
}
// Вывод: Подстрока найдена на позиции 7

Пример с Count:

str := "Hello, World! World!"
substr := "World"
count := strings.Count(str, substr)
fmt.Printf("Подстрока встречается %d раз(а)\n", count)
// Вывод: Подстрока встречается 2 раз(а)

Учёт регистра

Все функции из пакета strings по умолчанию чувствительны к регистру. Если нужно выполнить поиск без учёта регистра, преобразуйте обе строки к нижнему или верхнему регистру с помощью strings.ToLower или strings.ToUpper:

str := "Hello, World!"
substr := "world"  // обратите внимание: маленькая буква w

// Чувствительный к регистру — вернёт false
fmt.Println(strings.Contains(str, substr)) // false

// Без учёта регистра
lowerStr := strings.ToLower(str)
lowerSubstr := strings.ToLower(substr)
fmt.Println(strings.Contains(lowerStr, lowerSubstr)) // true

Для частого использования можно написать небольшую вспомогательную функцию:

func containsCaseInsensitive(s, substr string) bool {
    return strings.Contains(
        strings.ToLower(s),
        strings.ToLower(substr),
    )
}

Производительность

Все три функции (Contains, Index, Count) внутри реализованы с использованием алгоритма Рабина-Карпа или аналогичных эффективных алгоритмов поиска подстрок. Для большинства практических задач разница в производительности незначительна. Однако:

  • strings.Contains — самая быстрая, если нужно только узнать, есть ли подстрока (она останавливается при первом же совпадении).
  • strings.Index — немного медленнее, так как вычисляет точную позицию.
  • strings.Count — самая медленная, так как сканирует всю строку полностью.

Поэтому для проверки наличия подстроки всегда используйте strings.Contains.

Проверка нескольких подстрок

Если нужно проверить, содержит ли строка хотя бы одну из нескольких подстрок, можно использовать цикл или функцию strings.ContainsAny (но она проверяет отдельные символы, а не подстроки). Для подстрок лучше написать так:

str := "Hello, Go!"
substrings := []string{"Hello", "Python", "Java"}

found := false
for _, sub := range substrings {
    if strings.Contains(str, sub) {
        found = true
        break
    }
}

if found {
    fmt.Println("Строка содержит хотя бы одну из подстрок")
}

Или используйте strings.ContainsAny для поиска любого из набора символов (не подстрок):

str := "Hello, Go!"
// Проверяет, содержит ли str хотя бы один из символов 'a', 'b', 'c'
fmt.Println(strings.ContainsAny(str, "abc")) // true (буква 'o' не входит, но 'l' входит? нет, 'l' не входит в "abc")
// Правильный пример: ищем символы 'H', 'e', 'l'
fmt.Println(strings.ContainsAny(str, "Hel")) // true

Заключение

Для 99% случаев используйте strings.Contains. Это идиоматичный, эффективный и читаемый способ проверки наличия подстроки в Go. Помните про регистр и выбирайте подходящую функцию под конкретную задачу.

<<>>

Регулярные выражения для сложного поиска

Если вам нужно выполнить более сложный поиск (например, с шаблонами, группами символов или повторениями), можно использовать пакет regexp. Однако для простой проверки на наличие подстроки это избыточно и медленнее, чем strings.Contains.

Пример с регулярным выражением:

package main

import (
    "fmt"
    "regexp"
)

func main() {
    str := "Hello, World! My email is test@example.com"
    
    // Проверяем, содержит ли строка email-адрес (упрощённый паттерн)
    matched, _ := regexp.MatchString(`\w+@\w+\.\w+`, str)
    fmt.Println("Содержит email:", matched) // true
    
    // Поиск подстроки с префиксом "W" и любыми символами после
    matched2, _ := regexp.MatchString(`W.*d`, str)
    fmt.Println("Содержит 'W...d':", matched2) // true
}

Когда использовать regexp вместо strings.Contains

  • Поиск по шаблону (например, все слова, начинающиеся с заглавной буквы).
  • Поиск с игнорированием регистра без преобразования строк (флаг (?i)).
  • Проверка формата строки (телефон, email, дата).

Пример с игнорированием регистра через regexp:

matched, _ := regexp.MatchString(`(?i)world`, "Hello, WORLD!")
fmt.Println(matched) // true

Важно: Если вам нужна только проверка наличия фиксированной подстроки, всегда используйте strings.Contains. Регулярные выражения стоит применять только для нетривиальных паттернов, так как они работают медленнее и сложнее в отладке.