В этой статье я покажу вам, как формировать строки и работать с ними в Go. Как и в большинстве современных языков, большая часть функциональных возможностей строкового типа данных обеспечивается библиотекой или, в случае Go, пакетом. Этот пакет называется strings, и я продемонстрирую несколько функций из этого пакета.

Строковый тип данных

Строки Go - это неизменяемые последовательности байтов. Неизменяемость означает, что после создания строка не может быть изменена, хотя, если строка хранится в переменной, эту переменную можно изменить.

Строки формируются путем помещения байтов, которые вы хотите рассматривать как строку, между двойными кавычками. Сформированная таким образом строка называется строковым литералом, который можно использовать в присвоении или в качестве аргумента функции.

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

name := "Dennis Ritchie"
var greeting string = "Hello, world!"

В следующем разделе я продемонстрирую, что значит неизменяемость строки.

Основные операции со строками

Первая операция, которую я продемонстрирую, - это функция len. Эта функция возвращает количество символов в строке. Вот пример:

func main() {
  greeting := "Hello, world!"
  fmt.Printf("The length of greeting is %d.\n", len(greeting))
}

Строки Go индексируются как массивы и как срезы. Я могу отобразить каждый байт строки, используя индексированный цикл for (вы также можете использовать функцию диапазона):

func main() {
  greeting := "Hello, world!"
  for i:=0; i<len(greeting); i++ {
    fmt.Println(greeting[i])
  }
}

Позже я покажу вам, как преобразовать байт в буквенный символ.

Несмотря на то, что вы можете получить доступ к каждому байту строки через его индекс, вы не можете изменить байт таким образом:

greeting[1] = "E"

Это вызывает панику и демонстрирует неизменность строк в Go.

Я могу разбить нить на части, разрезая нить по кусочкам:

func main() {
  greeting := "Hello, world!"
  fmt.Println(greeting[0:5]) // displays Hello
}

Все разные способы работы со срезами работают со строками.

Несколько строк можно «склеить» вместе с помощью конкатенации. Оператор конкатенации Go - +. Вот пример:

func main() {
  first := "Ken"
  last  := "Thompson"
  full := first + " " + last
  fmt.Println(full) // displays Ken Thompson
}

Как и в других языках, Go имеет специальные последовательности байтовых символов, которые можно встраивать в строки. Эти последовательности аналогичны последовательностям C и C ++, например \n для новой строки и \t для табуляции.

Пакет строк

Большая часть функций, необходимых для работы со строками, находится в пакете strings. В этом разделе я расскажу о некоторых функциях этого пакета.

Первая функция, которую мы рассмотрим, - contains. Эта логическая функция проверяет, содержит ли строка указанную подстроку. Вот пример:

package main
import (
  "fmt"
  "strings"
)
func main() {
  address := "3000 W. Scenic Drive"
  if strings.Contains(address, "Scenic") {
    fmt.Print("Located on Scenic Drive")
  } else {
      fmt.Print("Located somewhere else.")
  }
}

Следующая функция, которую я рассмотрю, - это count. Эта функция возвращает количество вхождений указанной подстроки в строку. Вот как это работает:

package main
import (
  "fmt"
  "strings"
)
func main() {
  s := "now is the time for all good people"
  os := strings.Count(s, "o")
  fmt.Println(s)
  fmt.Printf("There are %d o's in the string.\n", os)
}

Далее идет функция fields . Эта функция разбивает строку вокруг последовательности из одного или нескольких символов пробела, возвращая часть оставшихся подстрок. Пример этой функции показан ниже:

package main
import (
  "fmt"
  "strings"
)
func main() {
  s := "   now is   the     time for    all good       people"
  words := strings.Fields(s)
  fmt.Println(words)
}

Функция index возвращает позицию индекса подстроки в строке или -1, если подстрока не найдена в строке. Вот программа, которая использует эту функцию:

package main
import (
  "fmt"
  "strings"
)
func main() {
  s := "now is the time for all good people"
  foundAt := strings.Index(s, "time")
  if foundAt > -1 {
    fmt.Printf("time is found at position %d in the string.\n",
               foundAt)
  } else {
    fmt.Println("time is not found in string.")
  }
}

Следующая описываемая мною функция используется для объединения нескольких подстрок в одну строку. Эта функция называется join. Первый аргумент - это фрагмент, содержащий набор строк. Второй аргумент - это разделитель, который будет помещен между каждой строкой в ​​срезе.

Вот пример:

package main
import (
  "fmt"
  "strings"
)
func main() {
  sl := []string{"now","is","the","time"}
  joined := strings.Join(sl," ")
  fmt.Println(joined)
}

Функция repeat повторяет строку указанное количество раз:

package main
import (
  "fmt"
  "strings"
)
func main() {
  fmt.Println(strings.Repeat("ei", 2) + "o") // displays eieio
}

Функция replace заменит первые n вхождений подстроки другой подстрокой и вернет новую строку. Если n меньше 0, будет выполнено столько замен, сколько возможно.

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

package main
import (
  "fmt"
  "strings"
)
func main() {
  misspellings := "recieve decieve reciever deciever believe"
  fmt.Println(misspellings)
  count := 1
  spellings := strings.Replace(misspellings, "cie", "cei",
                               count)
  fmt.Println(spellings)
}

Если вы хотите заменить все подстроки в строке другой подстрокой, используйте функцию ReplaceAll. Вот как это работает:

package main
import (
  "fmt"
  "strings"
)
func main() {
  misspellings := "recieve decieve reciever deciever believe"
  fmt.Println(misspellings)
  spellings := strings.ReplaceAll(misspellings, "cie", "cei")
  fmt.Println(spellings)
}

Следующая функция, которую я собираюсь продемонстрировать, - это функция, сестра join. Функция split принимает строку и разделитель и возвращает фрагмент, содержащий весь текст между разделителями. Если строка равна “Meredith,Allison,Mason”, а разделитель равен, то возвращаемый фрагмент - [Meredith Allison Mason]. Вот код для этого:

package main
import (
  "fmt"
  "strings"
)
func main() {
  siblings := "Meredith,Allison,Mason"
  splitUp := strings.Split(siblings, ",")
  fmt.Println(splitUp)
}

Следующие две функции можно использовать для изменения регистра строки. ToLower заменяет строку на нижний регистр, а ToUpper изменяет регистр на верхний регистр. Вот пример:

package main
import (
  "fmt"
  "strings"
)
func main() {
  s := "I'M YELLING"
  fmt.Println(strings.ToLower(s))
  s = "i'm whispering"
  fmt.Println(strings.ToUpper(s))
}

Последний пример, который я продемонстрирую, - это функция TrimSpace, которая обрезает все пробелы от начала и до конца строки. Вот как работает эта функция:

package main
import (
  "fmt"
  "strings"
)
func main() {
  name := "  Dennis Ritchie    "
  fmt.Println(name)
  name = strings.TrimSpace(name)
  fmt.Println(name)
}

Это всего лишь несколько примеров функций из пакета strings, и вам следует просмотреть документ Go в пакете, чтобы увидеть полный список.

Преобразования строк

Очень кратко отмечу еще один пакет - strconv. Этот пакет содержит функции для преобразования строк в числа и числа в строки, а также других преобразований.

Функция Itoa преобразует целое число в строку. Вот пример его использования:

package main
import (
  "fmt"
  "strconv"
)
func main() {
  fmt.Print("Hello, Agent " + strconv.Itoa(99) + ".")
}

Функция Atoi преобразует строку в целое число. Он также возвращает объект ошибки, который необходимо обработать при вызове функции, как вы можете видеть в примере ниже:

package main
import (
  "fmt"
  "strconv"
)
func main() {
  number1, err := strconv.Atoi("1")
  number2, err1 := strconv.Atoi("2")
  result := number1 + number2
  if err == nil && err1 == nil {
    fmt.Printf("%d + %d = %d.\n", number1, number2, result)
  }
}

В пакете strconv есть еще несколько функций, но это две из тех, которые вы будете использовать чаще всего.

Есть еще два пакета, которые относятся к строкам Go, байтам и Unicode, но я не собираюсь их здесь описывать. Если вам интересны эти пакеты, обратитесь к документации Go.

Строки и символы

Вы должны заметить, что в Go нет типа char. Для этой цели используется тип byte, как мы видели в начале этой статьи, когда я перебирал строку, используя индексированный цикл for. В конце статьи я покажу вам, как преобразовать байт в строку с помощью функции Printf:

func main() {
  greeting := "Hello, world!"
  for i:=0; i<len(greeting); i++ {
    fmt.Printf("%c\n", greeting[i])
  }
}

Спецификация формата% c изменит байт на символ, чтобы отобразить его для вывода.

Го и струнные

В Go есть хорошая современная реализация строк. В этой статье я рассмотрел то, что считаю наиболее важным аспектом использования строк в Go, но не все. Я особенно избегал говорить о том, как Go использует Unicode при реализации строк. Если вы хотите узнать об этом больше, я предлагаю прочитать книгу Алана Донована и Брайана Кернигана Язык программирования Go.

Спасибо за чтение, напишите мне с комментариями и предложениями.