Пакет strings
Одни и те же задачи с текстом встречаются почти в каждой программе: найти слово, сменить регистр, разбить фразу на части. Для них в стандартной библиотеке Go есть пакет strings.
Вернёмся к картинке с фишками. Пакет strings — набор инструментов для ряда фишек: найти в нём узор, заменить фишки, разрезать ряд на куски и сложить обратно. Старый ряд ни один инструмент не трогает — строки неизменяемы. Каждый собирает новый ряд и отдаёт его вам. Давайте посмотрим на главные инструменты в деле:
package main
import (
"fmt"
"strings"
)
func main() {
s := "Go is fun"
fmt.Println(strings.Contains(s, "is")) // true
fmt.Println(strings.ToLower(s)) // go is fun
words := strings.Split(s, " ")
fmt.Println(len(words), strings.Join(words, "-")) // 3 Go-is-fun
}Разберём по строкам:
Containsнаходит"is"в строке и возвращаетtrue.ToLowerвозвращает копию строки строчными буквами.Splitрежет фразу по пробелам на три слова.Joinсклеивает их обратно через дефис, и программа печатает3 Go-is-fun.
Поиск подстроки
strings.Contains(s, sub) возвращает true, если подстрока sub встречается в s. Поиск учитывает регистр:
strings.Contains("Go", "go")возвращаетfalse;strings.Contains("привет", "иве")возвращаетtrue.
Смена регистра
strings.ToLower и strings.ToUpper переводят строку в нижний и верхний регистр, кириллицу тоже: strings.ToUpper("мир") возвращает "МИР".
Каждая функция пакета возвращает результат новой строкой, а исходная остаётся прежней: строки неизменяемы. Чтобы сравнить слова без учёта регистра, сравнивают их версии в нижнем регистре: strings.ToLower("Go") == strings.ToLower("GO") даёт true.
Разбить и склеить
Три функции пакета режут строку на части и собирают части обратно. Давайте разберём каждую.
strings.Split
strings.Split(s, sep) режет строку по разделителю и возвращает срез строк []string. Она режет по каждому разделителю, поэтому:
- если пробелы стоят подряд или по краям,
Splitвозвращает между ними пустые строки; - если разделителя в строке нет,
Splitвозвращает срез из одного элемента — всей строки.
Split выбирают, когда разделитель задан точно и важна каждая часть между разделителями, даже пустая.
strings.Join
strings.Join(words, sep) делает обратное: склеивает срез в одну строку и вставляет разделитель между элементами. Она принимает только срез строк []string. Пустой разделитель "" склеивает части вплотную.
strings.Fields
strings.Fields(s) режет по любому числу пробелов и пустых строк не возвращает. Пробелом она считает и табуляцию, и перевод строки. Fields выбирают, когда нужны слова, а сколько между ними пробелов, неважно. Сравним её со Split на одной строке:
s := " go is "
fmt.Println(len(strings.Split(s, " ")), len(strings.Fields(s))) // 5 2
fmt.Println(strings.Fields(s)) // [go is]
fmt.Println(strings.Join([]string{"go", "is"}, "")) // goisДавайте разберём пример по строкам:
- Первый
Printlnпечатает5 2:Splitвернул пять частей, аFields— два слова. - Второй
Printlnпечатает[go is]:Fieldsоставил только слова. - Третий
Printlnпечатаетgois: пустой разделитель склеил части вплотную.
Пять частей Split такие:
- пустая строка до первого пробела;
"go";- пустая строка между пробелами;
"is";- пустая строка в конце.
Поэтому strings.Join(strings.Fields(s), " ") убирает из фразы лишние пробелы: Fields находит слова, а Join ставит между ними ровно по одному пробелу.
Сборка строки в цикле
На собеседовании любят спрашивать, почему длинную строку не собирают через += в цикле. Давайте сравним два способа.
Оператор +=
Строка неизменяема, поэтому каждый s += w создаёт новую строку и копирует в неё все накопленные байты. Для тысячи слов это тысяча копий, и каждая длиннее предыдущей. Для нескольких частей вне цикла оператор + подходит: копий там мало.
strings.Builder
Тип strings.Builder копит байты в одном месте, как append копит элементы среза:
words := []string{"go", "is", "fun"}
var b strings.Builder
for _, w := range words {
b.WriteString(w)
}
fmt.Println(b.String()) // goisfunДавайте разберём пример по строкам:
var b strings.Builderзаводит пустой сборщик: его нулевое значение уже готово к работе.b.WriteString(w)в цикле дописывает слово в конец, не копируя заново всё, что накоплено раньше.b.String()возвращает готовую строку, и программа печатаетgoisfun.
Частая ошибка
Функцию пакета вызывают и ждут, что изменится сама строка:
s := "go"
strings.ToUpper(s) // ✗
fmt.Println(s) // goПрограмма собирается и печатает go: ToUpper вернула новую строку, и её никто не сохранил. В отличие от append, здесь компилятор не сообщает о неиспользованном результате, и ошибку видно только по выводу. Результат присваивают переменной: s = strings.ToUpper(s).
Главное
- Функции пакета
stringsне меняют строку, а возвращают новую — результат нужно сохранить. Splitрежет по каждому разделителю и оставляет пустые строки, аFieldsрежет по пробелам без пустых строк.Joinсклеивает срез строк через разделитель.- Длинную строку в цикле собирают через
strings.Builder: каждый+=копирует всю строку заново.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.