Основы работы со строками в Go

GolangGolangBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В предыдущем уроке мы узнали, что символы в Go кодируются с использованием UTF-8 и хранятся в виде типов byte или rune. Теперь давайте обсудим строки, которые представляют собой коллекции символов. Давайте вместе исследуем эту тему.

Точки знания:

  • Что такое строка
  • Создание строки
  • Объявление строки
  • Общие функции для работы со строками
  • Доступ к элементам строки

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL go(("Golang")) -.-> go/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) go(("Golang")) -.-> go/AdvancedTopicsGroup(["Advanced Topics"]) go(("Golang")) -.-> go/BasicsGroup(["Basics"]) go(("Golang")) -.-> go/DataTypesandStructuresGroup(["Data Types and Structures"]) go(("Golang")) -.-> go/FunctionsandControlFlowGroup(["Functions and Control Flow"]) go/BasicsGroup -.-> go/values("Values") go/BasicsGroup -.-> go/variables("Variables") go/DataTypesandStructuresGroup -.-> go/strings("Strings") go/FunctionsandControlFlowGroup -.-> go/for("For") go/FunctionsandControlFlowGroup -.-> go/functions("Functions") go/ObjectOrientedProgrammingGroup -.-> go/struct_embedding("Struct Embedding") go/AdvancedTopicsGroup -.-> go/text_templates("Text Templates") go/AdvancedTopicsGroup -.-> go/number_parsing("Number Parsing") subgraph Lab Skills go/values -.-> lab-149069{{"Основы работы со строками в Go"}} go/variables -.-> lab-149069{{"Основы работы со строками в Go"}} go/strings -.-> lab-149069{{"Основы работы со строками в Go"}} go/for -.-> lab-149069{{"Основы работы со строками в Go"}} go/functions -.-> lab-149069{{"Основы работы со строками в Go"}} go/struct_embedding -.-> lab-149069{{"Основы работы со строками в Go"}} go/text_templates -.-> lab-149069{{"Основы работы со строками в Go"}} go/number_parsing -.-> lab-149069{{"Основы работы со строками в Go"}} end

Что такое строка

В первой программе на Go, которую мы изучили, мы выводили строку "hello, world".

Строка - это базовый тип данных в Go, также известный как строковый литерал (string literal). Ее можно рассматривать как коллекцию символов, занимающую непрерывный блок памяти. Этот блок памяти может хранить любые типы данных, такие как буквы, текст, эмодзи и т.д.

Однако, в отличие от других языков, строки в Go являются неизменяемыми (immutable) и не могут быть модифицированы. Это означает, что после создания строки вы не можете изменить ее отдельные символы. Если вам нужна модифицированная версия строки, вы должны создать новую.

Создание строки

Строки можно объявлять различными способами. Рассмотрим первый метод. Создайте новый файл с именем string.go:

touch ~/project/string.go

Напишите следующий код:

package main

import "fmt"

func main() {
    // Use the var keyword to create a string variable a
    var a string = "labex"
    a = "labex" // Assign "labex" to variable a

    // Declare variable b and assign its value
    var b string = "labs"

    // Type declaration can be omitted
    var c = "Monday"

    // Use := for quick declaration and assignment
    d := "Sunday"
    fmt.Println(a, b, c, d)
}

В приведенном выше коде показано, как создавать строки с использованием ключевого слова var и оператора :=. Если вы присваиваете значение при создании переменной с помощью var, можно опустить объявление типа, как это показано при создании переменной c. Оператор := является сокращенной формой объявления и инициализации переменной в Go. Он автоматически определяет тип переменной по значению, которое ей присваивается. Использование этого оператора делает код более компактным.

go run string.go

Ожидаемый вывод выглядит следующим образом:

labex labs Monday Sunday

Объявление строки

В большинстве случаев для объявления строк используются двойные кавычки "". Преимущество двойных кавычек заключается в том, что они позволяют использовать экранированные последовательности. Например, в следующей программе мы используем экранированную последовательность \n для создания новой строки:

package main

import "fmt"

func main() {
    x := "linux\nlabex"
    fmt.Println(x)
}
go run string.go

Ожидаемый вывод выглядит следующим образом:

linux
labex

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

Символ Описание
\n Новая строка
\r Возврат каретки
\t Табуляция
\b Backspace
\\ Обратный слэш
\' Одинарная кавычка
\" Двойная кавычка

Если вы хотите сохранить исходный формат текста или вам нужно использовать многострочный текст, вы можете использовать обратные кавычки (backticks):

package main

import "fmt"

func main() {
    // Output the ASCII art of "labex"
    ascii := `
        ##        #####   #########  ########## ##    #
        ##       ##  ##  ##    ## ##       ##  #
        ##      ##    ## #########  #########    ##
        ##      ########## ##    ## ##        ##
        ##      ##    ## ##    ## ##       ##  #
        ########## ##    ## #########  ########## ##    #`
    fmt.Println(ascii)
}

Обратные кавычки обычно используются в подсказках, HTML-шаблонах и других случаях, когда нужно сохранить исходный формат вывода. Текст внутри обратных кавычек рассматривается как необработанный строковый литерал (raw string literal), то есть экранированные последовательности не интерпретируются. Это позволяет удобно включать многострочный текст и специальные символы без необходимости экранирования.

Получение длины строки

В предыдущем уроке мы узнали, что английские символы и общие знаки пунктуации занимают один байт.

Поэтому в Go можно использовать функцию len() для получения длины строки в байтах. Если в строке нет символов, занимающих несколько байт, функция len() позволяет точно измерить длину строки.

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

Рассмотрим пример. Напишите следующий код в файл string.go:

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    // Declare two empty strings using var and :=
    var a string
    b := ""

    c := "labex"
    d := "abc"


    // Output byte length
    fmt.Printf("The value of a is %s, the byte length of a is: %d\n", a, len(a))
    fmt.Printf("The value of b is %s, the byte length of b is: %d\n", b, len(b))
    fmt.Printf("The value of c is %s, the byte length of c is: %d\n", c, len(c))
	fmt.Printf("The value of d is %s, the byte length of d is: %d\n", d, len(d))


    // Output string length
    fmt.Printf("The length of c is: %d\n", utf8.RuneCountInString(c))
	fmt.Printf("The length of d is: %d\n", utf8.RuneCountInString(d))
}
go run string.go

Ожидаемый вывод выглядит следующим образом:

The value of a is, the byte length of a is: 0
The value of b is, the byte length of b is: 0
The value of c is labex, the byte length of c is: 5
The value of d is abc, the byte length of d is: 3
The length of c is: 5
The length of d is: 3

В программе мы сначала объявили две пустые строки и строки labex и abc. Можно заметить, что их длина в байтах и фактическая длина совпадают, так как они содержат только однобайтовые символы.

Доступ к элементам строки

Поскольку строки по сути представляют собой последовательности байтов, мы можем получить доступ к отдельным байтам или символам в строке с помощью их индекса. В Go индексация строк начинается с 0, как и в массивах.

Однако важно помнить, что доступ к элементам строки по индексу возвращает byte, а не символ (rune). Если строка содержит многобайтовые символы, при использовании индексов нужно быть осторожными, так как вы можете не получить целый символ. Чтобы безопасно получить отдельные символы (rune), можно использовать цикл for...range для перебора строки, который корректно обрабатывает кодировку UTF - 8.

Давайте добавим следующий код в файл string.go:

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	str := "Hello, world!"

	// Accessing byte by index
	fmt.Printf("Byte at index 0: %c\n", str[0]) // Output: H
	fmt.Printf("Byte at index 7: %c\n", str[7]) // Output: w

    // Iterate using for...range to safely get runes
    fmt.Println("Iterating through runes:")
    for index, char := range str {
        fmt.Printf("Index: %d, Char: %c\n", index, char)
    }

	// Getting the number of runes
	fmt.Printf("Number of runes (characters) in the string: %d\n", utf8.RuneCountInString(str))

}

Теперь запустим программу:

go run string.go

Вывод будет следующим:

Byte at index 0: H
Byte at index 7: w
Iterating through runes:
Index: 0, Char: H
Index: 1, Char: e
Index: 2, Char: l
Index: 3, Char: l
Index: 4, Char: o
Index: 5, Char:,
Index: 6, Char:
Index: 7, Char: w
Index: 8, Char: o
Index: 9, Char: r
Index: 10, Char: l
Index: 11, Char: d
Index: 12, Char:!
Number of runes (characters) in the string: 13

В этом выводе можно заметить, что:

  1. Доступ к строке по определенному индексу возвращает байт.
  2. Использование цикла for...range позволяет правильно перебирать символы (rune).

В этом примере показано ключевое различие между байтом и символом (rune) и важность использования подходящих методов при работе с символами в строках Go.

Преобразование строк и целых чисел

Мы можем использовать функции из пакета strconv для преобразования между строками и целыми числами:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    // Declare a string a and an integer b
    a, b := "233", 223

    // Use Atoi to convert a string to an integer
    c, _ := strconv.Atoi(a)

    // Use Sprintf and Itoa functions respectively
    // to convert an integer to a string
    d1 := fmt.Sprintf("%d", b)
    d2 := strconv.Itoa(b)

    fmt.Printf("The type of a: %T\n", a)   // string
    fmt.Printf("The type of b: %T\n", b)   // int
    fmt.Printf("The type of c: %T\n", c)   // int
    fmt.Printf("The type of d1: %T\n", d1) // string
    fmt.Printf("The type of d2: %T\n", d2) // string
}
go run string.go

Ожидаемый вывод выглядит следующим образом:

The type of a: string
The type of b: int
The type of c: int
The type of d1: string
The type of d2: string

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

func Sprintf(format string, a...interface{}) string

format - это строка с экранированными последовательностями, a - это константа или переменная, которая предоставляет значения для экранированных последовательностей, а ... означает, что может быть несколько переменных того же типа, что и a. Строка после функции означает, что Sprintf возвращает строку. Вот пример использования этой функции:

a = fmt.Sprintf("%d+%d=%d", 1, 2, 3)
fmt.Println(a) // 1+2=3

В этом фрагменте кода в format передаются три целочисленные переменные 1, 2 и 3. Экранированный символ %d для целых чисел в format заменяется целочисленными значениями, и функция Sprintf возвращает результат после замены, 1+2=3.

Также обратите внимание, что при использовании strconv.Atoi() для преобразования строки в целое число функция возвращает два значения: преобразованное целое число val и код ошибки err. Поскольку в Go, если вы объявляете переменную, вы должны ее использовать, мы можем использовать подчеркивание _ для игнорирования переменной err.

Когда strconv.Atoi() выполняет преобразование правильно, err возвращает nil. Когда при преобразовании возникает ошибка, err возвращает сообщение об ошибке, а значение val будет равно 0. Вы можете изменить значение строки a и заменить подчеркивание обычной переменной, чтобы попробовать это самостоятельно. Это хорошая практика обработки ошибок, которая является важной частью программирования на Go.

Конкатенация строк

Самый простой способ объединить две или более строк - использовать оператор +. Также можно использовать функцию fmt.Sprintf() для конкатенации строк. Рассмотрим пример:

package main

import (
    "fmt"
)

func main() {
    a, b := "lab", "ex"
    // Concatenate using the simplest method, +
    c1 := a + b
    // Concatenate using the Sprintf function
    c2 := fmt.Sprintf("%s%s", a, b)
    fmt.Println(a, b, c1, c2) // lab ex labex labex
}
go run string.go

Ожидаемый вывод выглядит следующим образом:

lab ex labex labex

В программе мы также использовали функцию Sprintf() из пакета fmt для конкатенации строк и вывода результатов. Оба метода являются распространенными способами конкатенации строк, и выбор между ними часто зависит от читаемости и личных предпочтений.

Удаление начальных и конечных пробелов из строки

Мы можем использовать функцию strings.TrimSpace для удаления начальных и конечных пробелов из строки. Функция принимает строку в качестве входных данных и возвращает строку с удаленными начальными и конечными пробелами. Формат выглядит следующим образом:

func TrimSpace(s string) string

Вот пример:

package main

import (
    "fmt"
    "strings"
)

func main() {
    a := " \t \n  labex \n \t labs"
    fmt.Println(strings.TrimSpace(a))
}
go run string.go

Ожидаемый вывод выглядит следующим образом:

labex
         labs

Обратите внимание, что strings.TrimSpace() удаляет только пробелы в начале и в конце строки, а пробелы внутри строки остаются без изменений.

Итог

Подведем итог тому, что мы узнали в этом уроке:

  • Связь между строками и символами.
  • Два способа объявления строк: с использованием двойных кавычек и обратных кавычек.
  • Доступ к элементам строки с помощью индекса (доступ к байтам) и for...range (доступ к символам rune).
  • Получение длины строки с помощью len() (длина в байтах) и utf8.RuneCountInString (длина в символах/rune).
  • Преобразование строк и целых чисел с использованием strconv.Atoi() и strconv.Itoa().
  • Конкатенация строк с использованием оператора + и fmt.Sprintf().
  • Удаление начальных и конечных пробелов из строки с использованием strings.TrimSpace().

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