Avatar ·

В Go нет функций strings.HasPrefix и strings.HasSuffix? Как проверить начало и конец строки?

Всем привет! Я только начал изучать Go после Python и столкнулся с неожиданной проблемой. В Python я привык писать: ```python if "hello world".startswith("hello"): print("Начинается с hello") ``` Или: ```python if "file.txt".endswith(".txt"): print("Это текстовый файл") ``` Но когда я попытался сделать то же самое в Go, я не нашёл встроенных методов `startswith` или `endswith`. Я посмотрел в документации по пакету `strings` и нашёл только `Contains`, `TrimPrefix`, `TrimSuffix`, но ничего похожего на прямую проверку начала или конца строки. Мне нужно написать простой парсер логов, где я проверяю, начинается ли строка с `"[ERROR]"` или заканчивается на `"CRITICAL"`. Пока я написал костыль: ```go line := "[ERROR] something went wrong" if len(line) >= 7 && line[:7] == "[ERROR]" { fmt.Println("Это ошибка") } ``` Но это выглядит ужасно и ненадёжно. Неужели в Go действительно нет нормальных функций для проверки начала и конца строки? Или я что-то упускаю? Подскажите, как правильно решить эту задачу на Go. <<>>

Ответ: В Go есть функции strings.HasPrefix и strings.HasSuffix

Не волнуйтесь, в Go действительно есть нужные вам функции, просто они называются немного иначе, чем в Python. Пакет strings предоставляет функции HasPrefix и HasSuffix для проверки начала и конца строки соответственно. Они полностью решают вашу задачу без необходимости писать костыли с нарезкой строк.

Вот как вы можете использовать их в вашем примере с парсингом логов:

package main

import (
    "fmt"
    "strings"
)

func main() {
    line := "[ERROR] something went wrong"
    
    if strings.HasPrefix(line, "[ERROR]") {
        fmt.Println("Это ошибка")
    }
    
    // Проверка конца строки
    message := "System failure: CRITICAL"
    if strings.HasSuffix(message, "CRITICAL") {
        fmt.Println("Критическая ошибка")
    }
    
    // Пример с разными префиксами
    logLines := []string{
        "[INFO] server started",
        "[WARN] disk space low",
        "[ERROR] connection lost",
        "[DEBUG] variable x = 42",
    }
    
    for _, log := range logLines {
        switch {
        case strings.HasPrefix(log, "[ERROR]"):
            fmt.Println("Ошибка:", log)
        case strings.HasPrefix(log, "[WARN]"):
            fmt.Println("Предупреждение:", log)
        case strings.HasPrefix(log, "[INFO]"):
            fmt.Println("Информация:", log)
        default:
            fmt.Println("Другое:", log)
        }
    }
}

Ключевые особенности:

  • HasPrefix(s, prefix string) bool — проверяет, начинается ли строка s с подстроки prefix
  • HasSuffix(s, suffix string) bool — проверяет, заканчивается ли строка s на подстроку suffix
  • Обе функции возвращают true или false
  • Регистр символов учитывается (как и в Python)
  • Работают с пустыми строками (пустая строка считается префиксом/суффиксом для любой строки)

Почему ваш "костыль" — плохая практика?

Ваш подход с line[:7] == "[ERROR]" имеет несколько проблем:

  • Если строка короче 7 символов, программа упадёт с panic (выход за границы среза)
  • Код менее читаемый и понятный
  • Не обрабатывает случай, когда строка пустая

Используйте strings.HasPrefix и strings.HasSuffix — это идиоматичный, безопасный и эффективный способ проверки начала и конца строк в Go. Функции реализованы нативно и работают быстро, без создания временных подстрок.

Также обратите внимание на родственные функции: strings.TrimPrefix и strings.TrimSuffix — они удаляют префикс/суффикс, если он есть, иначе возвращают исходную строку без изменений.

<<>>

Альтернативный подход: использование регулярных выражений

Хотя strings.HasPrefix и strings.HasSuffix — это стандартное и наиболее эффективное решение, в некоторых случаях вам может понадобиться более гибкая проверка, например, с игнорированием регистра или с использованием шаблонов. Для этого можно применить пакет regexp:

package main

import (
    "fmt"
    "regexp"
)

func main() {
    line := "[ERROR] something went wrong"
    
    // Проверка с игнорированием регистра
    pattern := `(?i)^\[error\]`
    matched, _ := regexp.MatchString(pattern, line)
    if matched {
        fmt.Println("Начинается с [ERROR] (регистр не важен)")
    }
    
    // Проверка суффикса с шаблоном
    line2 := "file.txt"
    pattern2 := `\.(txt|md)$`
    matched2, _ := regexp.MatchString(pattern2, line2)
    if matched2 {
        fmt.Println("Файл имеет расширение .txt или .md")
    }
}

Однако помните: регулярные выражения медленнее, чем встроенные функции HasPrefix/HasSuffix, и их следует использовать только тогда, когда нужна дополнительная гибкость (например, проверка нескольких вариантов сразу или игнорирование регистра). Для простых случаев всегда предпочитайте strings.HasPrefix и strings.HasSuffix.