Avatar ·

Как в Go проверить, начинается ли строка с определенной подстроки (аналог StartsWith)?

Всем привет! Я только начал изучать Go, перешел с JavaScript, и столкнулся с небольшой, но раздражающей проблемой. В JS я привык использовать метод `str.startsWith("prefix")`, который возвращает true или false. Очень удобно для проверки префиксов URL, файловых путей или префиксов команд. В Go я ожидал найти что-то похожее в пакете `strings`, но, к своему удивлению, не нашел метода `StartsWith`. Есть `HasPrefix`, `Contains`, `TrimPrefix`... но нет прямого аналога, который бы сразу давал булево значение. Вот пример кода, который я пытаюсь написать: ```go package main import ( "fmt" "strings" ) func main() { url := "https://example.com/api/v1/users" // Вот тут я не знаю, как правильно проверить // В JS было бы: url.startsWith("https://") if ??? { fmt.Println("Это HTTPS URL") } } ``` Я пробовал использовать `strings.HasPrefix(url, "https://")`, но мне кажется, что это не совсем то, что нужно, или я просто не понимаю идиоматичный способ в Go. Подскажите, пожалуйста, как правильно и «по-гошному» проверить, начинается ли строка с определенной подстроки? Есть ли какой-то стандартный метод, или нужно писать свою функцию? Спасибо! <<>>

Используйте strings.HasPrefix — это и есть StartsWith в Go

Вы почти у цели! В Go функция strings.HasPrefix — это полный аналог JavaScript метода startsWith(). Она возвращает bool (true/false) и работает именно так, как вы ожидаете. Не путайте её с strings.HasSuffix (проверка окончания) или strings.Contains (проверка вхождения в любом месте).

Вот исправленный пример вашего кода:

package main

import (
    "fmt"
    "strings"
)

func main() {
    url := "https://example.com/api/v1/users"
    
    // Правильный способ — используем HasPrefix
    if strings.HasPrefix(url, "https://") {
        fmt.Println("Это HTTPS URL")
    }
    
    // Ещё примеры:
    path := "/home/user/docs/file.txt"
    
    if strings.HasPrefix(path, "/home") {
        fmt.Println("Путь начинается с /home")
    }
    
    // Можно проверять и пустые строки
    empty := ""
    fmt.Println(strings.HasPrefix(empty, "")) // true — пустая строка считается префиксом для любой строки
    fmt.Println(strings.HasPrefix(empty, "a")) // false
}

Почему именно HasPrefix?

В Go стандартная библиотека следует принципу «явное лучше неявного». Название HasPrefix (имеет префикс) очень точно описывает действие: «проверяет, имеет ли строка данный префикс». Это более описательно, чем StartsWith. Вот полный список похожих функций из пакета strings:

  • strings.HasPrefix(s, prefix) — проверяет, начинается ли s с prefix (возвращает bool)
  • strings.HasSuffix(s, suffix) — проверяет, заканчивается ли s на suffix (bool)
  • strings.Contains(s, substr) — проверяет, содержит ли s substr в любом месте (bool)
  • strings.TrimPrefix(s, prefix) — удаляет prefix из начала s, если он есть (возвращает новую строку)
  • strings.CutPrefix(s, prefix) — (Go 1.20+) удаляет prefix и возвращает остаток + true/false

Важные нюансы

1. Регистрозависимость: HasPrefix чувствителен к регистру. HasPrefix("Hello", "he") вернёт false. Если нужна проверка без учёта регистра, используйте strings.EqualFold вместе с HasPrefix или преобразуйте строки:

// Проверка без учёта регистра
if strings.HasPrefix(strings.ToLower(url), "https://") {
    // ...
}

// Или через EqualFold (только для полного равенства, не для префикса)
// Для префикса без учёта регистра придётся написать свою функцию
func hasPrefixFold(s, prefix string) bool {
    return len(s) >= len(prefix) && strings.EqualFold(s[:len(prefix)], prefix)
}

2. Производительность: HasPrefix работает за O(n) по длине префикса (а не всей строки). Для коротких префиксов это очень быстро.

3. Пустая строка: HasPrefix("anything", "") всегда возвращает true, так как пустая строка считается префиксом для любой строки.

4. Идиоматичность: В Go принято использовать именно HasPrefix, а не оборачивать его в свою функцию типа StartsWith. Это стандартный паттерн, который поймёт любой Go-разработчик.

Альтернатива с Go 1.20+

Если вам нужно не только проверить, но и получить остаток строки после префикса, используйте strings.CutPrefix:

url := "https://example.com"
if rest, ok := strings.CutPrefix(url, "https://"); ok {
    fmt.Println("Префикс найден, остаток:", rest) // "example.com"
}

Так что смело используйте strings.HasPrefix — это и есть правильный, идиоматичный способ проверки начала строки в Go.

<<>>

Создание собственной обёртки для читаемости

Хотя strings.HasPrefix — это стандартный и рекомендуемый способ, в больших проектах иногда удобно создать свою функцию-обёртку с более привычным названием, особенно если в команде есть разработчики из других языков:

package main

import (
    "fmt"
    "strings"
)

// StartsWith — обёртка над HasPrefix для читаемости
func StartsWith(s, prefix string) bool {
    return strings.HasPrefix(s, prefix)
}

func main() {
    url := "https://example.com"
    
    // Теперь можно писать как в JS
    if StartsWith(url, "https://") {
        fmt.Println("HTTPS")
    }
    
    // Или даже так, если вы любите fluent-интерфейсы:
    // (но в Go это не очень идиоматично)
    type MyString string
    s := MyString(url)
    fmt.Println(s.StartsWith("https://")) // потребует метод на типе
}

Когда это оправдано:

  • В проектах с жёсткими корпоративными стандартами кода
  • При миграции кода с другого языка (Python, JS, Java)
  • Когда нужно добавить логирование или дополнительные проверки

Когда не стоит: В большинстве случаев лучше придерживаться стандартной библиотеки. Go-сообщество привыкло к HasPrefix, и лишние обёртки могут запутать других разработчиков. Кроме того, каждая обёртка — это дополнительный слой абстракции и потенциальное место для багов.

Если вы всё же решите создать свою функцию, обязательно задокументируйте её и укажите, что это просто обёртка над strings.HasPrefix.