Строка (String) — базовый тип в языке Golang, представляющий собой простую последовательность байтов, но обладающий специальными методами для работы с ними.
При этом в Golang входит базовый пакет strings
, который содержит основные (и достаточно простые) функций для работы со строковым типом данных. Эти функции похожи на типичные функции для работы со строками из других языков программирования, таких как C или C++.
Стоит также упомянуть базовый пакет fmt, используемый в примерах этой статьи для форматирования и вывода строк в консоль.
Как видно, помимо объявления строк Golang поддерживает обширный список возможностей для выполнения различных манипуляций над ними.
cloud
Прежде всего стоит сказать, что в Golang строки устроены несколько иначе, чем в Java, C++ или Python. По сути, это последовательность символов, каждый из которых имеет разный размер — то есть он может быть представлен одним или несколькими байтами в кодировке UTF-8.
Дело в том, что во времена изобретения языка C, символ в компьютере представлялся 7-битным кодом ASCII. Таким образом, строка представляла собой набор множества 7-битных символов ASCII.
Однако по мере роста использования компьютеров во всем мире 7-битной схемы ASCII стало недостаточно для поддержки символов других языков. Поэтому были предложены различные модели кодировки символов, такие как Unicode, UTF-8, UTF-16, UTF-32 и т. д.
Различные языки программирования имеют свою собственную схему кодировки символов. Например, Java изначально использует UTF-16. А вот Go, напротив, построен на кодировке UTF-8.
Именно благодаря ей строка Go может содержать универсальный текст, представляющий собой смесь любого существующего в мире языка — без какой-либо путаницы или любых других ограничений. При этом сами строки всегда статичны и имеют неизменный состав.
Существует несколько распространенных способов объявления (определения) строк в Go:
// явное определение через «var»
var variable1 = "some text"
// явное определение через «var» с указанием типа, но без значения
var variable2 string = "peace"
// более короткое определение
variable3 := "some text"
Строку можно также объявить без явного указания значения. В этом случае строковая переменная инициализируется нулевым значением — пустой строкой:
var variable4 string
При использовании двойных кавычек при создании строковой переменной Go будет интерпретировать специальные (экранированные) символы, написанные через слэш. Например, такие как /n
:
import "fmt"
...
var some_variable = "first line/nsecond line/nthird line"
fmt.Println(some_variable) // выводим строку в консоль
// ВЫВОД:
// first line
// second line
// third line
Для того, чтобы компилятор Go игнорировал спецсимволы и при этом сохранял оригинальное форматирование строк, нужно использовать одинарные (обратные) кавычки.
Вот пример объявления Golang строки с явным указанием форматирования:
import "fmt"
...
// переносы строк в переменной указывается явно без добавления спец символов
var some_variable = `first line
second line
third line`
fmt.Println(some_variable) // выводим строку в консоль
// ВЫВОД:
// first line
// second line
// third line
Обратите внимание, что для вывода строк в консоль используется пакет fmt
.
А в этом примере Go полностью игнорирует экранированные символы:
import "fmt"
...
var some_variable = `upper line /n lower line`
fmt.Println(some_variable) // выводим строку в консоль
// ВЫВОД: upper line /n lower line
Смысл существования специального типа String
в том, чтобы работать не с «сырой» последовательностью байтов, а со специальными методами, управляющими ей.
Однако, строго говоря, в Golang (в отличие от C и C++) строки изменять нельзя. Они статичны. Можно только получать доступ к конкретным символам по индексу:
import "fmt"
...
variable := "hello"
c := variable[0]
fmt.Printf("%c\n", c) // ВЫВОД: h
Тем не менее, есть большое количество способов создания новых строк из уже существующих.
Важно знать, что некоторые функции для преобразования строк требуют включения специального пакета strings
:
import "strings"
Самая базовая манипуляция — соединение (конкатенация) нескольких строк в одну целую. Выполняется с помощью оператора сложения:
import "fmt"
...
var variable1 = "hello"
var variable2 = "world"
var space = " "
var variable3 = variable1 + space + variable2
fmt.Println(variable3) // ВЫВОД: hello world
fmt.Println(variable2 + ", " + variable1) // ВЫВОД: world, hello
Обратите внимание, что вы не можете складывать строки с другими типами. Например, с числовыми:
fmt.Println("i am " + 24 + " years old") // ОШИБКА
Чтобы пример выше работал, необходимо использовать функцию преобразования типов. В данном случае из int
в string
:
import "fmt"
...
age := 24
fmt.Println("i am " + strconv.Itoa(age) + " years old") // ВЫВОД: i am 24 years old
fmt.Println("i am " + strconv.Itoa(24) + " years old") // ВЫВОД: i am 24 years old
Можно обрезать начальные и конечные символы строки, указав их в отдельном аргументе:
import (
"fmt"
"strings"
)
...
result := strings.Trim("xxxhello worldxxx", "xxx")
fmt.Println(result) // ВЫВОД: hello world
Можно также разбить строку на подстроки, указав разделитель:
import (
"fmt"
"strings"
)
...
result := strings.Split("hello world", " ")
fmt.Println(result) // ВЫВОД: [hello world]
Можно склеить несколько строк, размещенных в массиве, в одну целую строку, явно указав разделитель:
import (
"fmt"
"strings"
)
...
result := strings.Join([]string{"hello", "world"}, " ") // в качестве аргумента указывается массив строк
fmt.Println(result) // ВЫВОД: hello world
Тем не менее, использование функции склеивания или оператора сложения для конкатенации строк — не самое лучшее решение. Такой род операций каждый раз создает новую строку, снижая производительность.
Поэтому в Golang есть специальный оптимизированный инструмент для сбора целых строк из составных частей с указанием неких правил — Builder
:
import (
"fmt"
"strings"
)
...
builded := &strings.Builder{}
builded.WriteString("very")
builded.WriteString(" ")
builded.WriteString("long")
builded.WriteString(" ")
builded.WriteString("line")
fmt.Println(builded.String()) // ВЫВОД: very long line
Более подробно об этом типе можно узнать в официальной документации Go. Тем не менее, использование Builder
довольно тривиально, т.к. он не содержит большого числа методов.
Разделить строку, указав отдельным аргументом разделитель, можно следующим образом:
import (
"fmt"
"strings"
)
...
result := strings.Split("h-e-l-l-o", "-")
fmt.Println(result) // ВЫВОД: [h e l l o]
Также есть несколько вариантов замены определенной подстроки на другую подстроку:
import (
"fmt"
"strings"
)
...
result := strings.Replace("hello", "l", "|", 1) // заменяем первое вхождение
fmt.Println(result) // ВЫВОД: he|lo
result = strings.Replace("hello", "l", "|", -1) // заменяем все вхождения
fmt.Println(result) // ВЫВОД: he||o
Go предоставляет возможность переключать регистр строк — увеличивать или уменьшать символы:
import (
"fmt"
"strings"
)
...
result := strings.Replace("hello", "l", "|", 1) // заменяем первое вхождение
fmt.Println(result) // ВЫВОД: he|lo
result = strings.Replace("hello", "l", "|", -1) // заменяем все вхождения
fmt.Println(result) // ВЫВОД: he||o
Вы также можете превратить последовательность явно указанных байтов в полноценную строку и работать уже с ней:
import "fmt"
...
// последовательность байтов
any_bytes := []byte{0x47, 0x65, 0x65, 0x6b, 0x73}
// создаем строку
any_string := string(myslice1)
fmt.Println(any_string) // ВЫВОД: Geeks
Один из вариантов — проверить наличие некоторой подстроки в строке:
import (
"fmt"
"strings"
)
...
result := strings.Contains("world", "rl")
fmt.Println(result) // ВЫВОД: true
result := strings.Contains("world", "rrl")
fmt.Println(result) // ВЫВОД: false
Разумеется, классические операторы сравнения тоже приспособлены для поиска совпадений. Каждый из них посимвольно сравнивает строки в их лексическом порядке и с учетом длины:
import "fmt"
...
fmt.Println("hello" == "hello") // ВЫВОД: true
fmt.Println("hello" == "hello world") // ВЫВОД: false
fmt.Println("hello" > "hell") // ВЫВОД: true
fmt.Println("hello" > "lo") // ВЫВОД: false
Помимо проверки на наличия подстроки, можно проверить вхождение в строку определенного префикса или суффикса:
import (
"fmt"
"strings"
)
...
result := strings.HasPrefix("hello", "he")
fmt.Println(result) // ВЫВОД: true
result = strings.HasSuffix("hello", "lo")
fmt.Println(result) // ВЫВОД: true
result = strings.HasPrefix("hello", "el")
fmt.Println(result) // ВЫВОД: false
Можно получить индекс первого вхождения указанной подстроки:
import (
"fmt"
"strings"
)
...
result := strings.Index("hello", "el")
fmt.Println(result) // ВЫВОД: 1
result = strings.Index("hello", "le")
fmt.Println(result) // ВЫВОД: -1
Часто может потребоваться узнать длину строки с помощью специальной функции:
import "fmt"
...
length := len("hello")
fmt.Println(length) // ВЫВОД: 5
Так как Go использует кодировку UTF-8, длина строки соответствует количеству используемых байтов, а не число символов. Это связано с тем, что некоторые символы занимают 2 и более байта:
import "fmt"
...
length := len("привет") // указано 6 символов
fmt.Println(length) // ВЫВОД: 12
Например, вот так можно перебрать все байты на основе фактической (байтовой) длины строки:
import "fmt"
...
any_string := "привет"
// ВЫВОД: d0 bf d1 80 d0 b8 d0 b2 d0 b5 d1 82
for i := 0; i < len(any_string); i++ {
fmt.Printf("%x ", any_string[i])
// тут может быть некоторое действие
}
В некоторых практических случаях (в том числе и при определении схожести строк) может потребоваться последовательный ручной перебор всех символов строки. Сделать это можно с помощью цикла:
import "fmt"
...
for symbol_index, symbol_value := range "Hello For All Worlds" {
fmt.Printf("Value: %c; Index: %d\n", symbol_value, symbol_index)
// тут может быть некоторое действие
}
Уже знакомый вам пакет fmt
имеет специальные возможности для форматирования строк в момент их вывода в консоль. На самом деле его возможности не сильно отличаются от методов форматирования в других языках программирования — используются шаблоны (templates) и глаголы нотации (annotation verbs):
import "fmt"
...
// вывод переменной строки через подстановку %s
any_string := "hello"
result := fmt.Sprintf("%s world", any_string)
fmt.Println(result) // ВЫВОД: hello world
// вывод переменной числа через подстановку %d
any_number := 13
result = fmt.Sprintf("there are %d worlds!", any_number)
fmt.Println(result) // ВЫВОД: there are 10 worlds"
// вывод переменной логики через подстановку %t
any_boolean := true
result = fmt.Sprintf("this is the %t world!", any_boolean)
fmt.Println(result) // ВЫВОД: this is the true world!
Обратите внимание, что для форматирования используется функция Sprintf
, которая лишь возвращает строку, которая впоследствии выводится в консоль через Println
.
Возможно использование и более сложных шаблонов форматирования с использованием нескольких переменных:
import "fmt"
...
// вывод двух строк в одном шаблоне
first_string := "hello"
second_string := "world"
result := fmt.Sprintf("%s %s", first_string, second_string)
fmt.Println(result) // ВЫВОД: hello world
// вывод трех чисел в одном шаблоне
first_number := 10
second_number := 20
third_number := 30
result = fmt.Sprintf("%d and %d and %d", first_number, second_number, third_number)
fmt.Println(result) // ВЫВОД: 10 and 20 and 30
// вывод двух логических в одном шаблоне
first_boolean := true
second_boolean := false
result = fmt.Sprintf("if it is not %t therefore it means it is %t", first_boolean , second_boolean)
fmt.Println(result) // ВЫВОД: if it is not true therefore it means it is false
Вы можете также совместить несколько разных типов переменных в рамках одной функции форматирования:
import "fmt"
...
first_string := "hello"
second_number := 13
third_boolean := true
result := fmt.Sprintf("%s to all %d %t worlds", first_string, second_number, third_boolean)
fmt.Println(result) // ВЫВОД: hello to all 13 true worlds
Можно также выполнять особый вывод с преобразованием чисел в бинарный форма «нулей и единиц»:
import "fmt"
...
first_number := 13
second_number := 25
result := fmt.Sprintf("%b and %b", first_number, second_number)
fmt.Println(result) // ВЫВОД: 1101 and 11001
Подготовили для вас выгодные тарифы на облачные серверы
Итак. Go предоставляет небольшой, но вполне достаточный инструментарий для манипулирования строками, покрывающий большую часть потребностей разработчика.
Одна из важных вещей, которую следует понимать при использовании строк Go, — то, что мы условно называем отдельными элементами строки (символами), на самом деле является последовательностью байтов UTF-8. Это значит, что при работе со строками мы манипулируем значениями байтов.
Поэтому попытка (которая запрещена в Go) изменения двухбайтового символа на однобайтовых приводила бы сбою.
Тем не менее, пакеты Go изобилуют функциями «манипулирования» строками. Однако в этой вводной статье были продемонстрированы базовые, но наиболее используемые способы взаимодействия со строками Go.
Помните, что в большинстве случаев продвинутое использование строк требует подключения специального пакета strings
, а также задействования пакета fmt
для форматирования строк при выводе в консоль.
Полную и подробную информацию о всех существующих методах этого пакета можно найти в официальной документации Go в соответствующем разделе «strings».