#18.1 golang rune string Работа с символами строки, Руны: посимвольное чтение, Юникод, Uft-8, конкатенация, range()

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

Тип rune (Руны)

Если наша строка состоит не только из ASCII, где достаточно 1 байта на символ, а и из других символов (например, наша любимая кириллица), то для работы с отдельными символами нам потребуются руны, тип который используется в golang для работы с мультибайтным символами.

Тип для для рун определен так:

type rune = int32

-- т.е. как раз 4 байта для хранения символов юникода.

Обращение к отдельным символам

Для обращения к отдельным символам мы можем преобразовать строку к срезу рун:

package main

import "fmt"

func main() {
	s := "Привет!"
	runes := []rune(s) // тип для рун опр

	fmt.Println("Количество байт:", len(s)) // 13
	fmt.Println("Количество символов(рун):", len(runes)) // 7
	// чтобы получить конкретный символ как строку string
	// требуется явное обратное преоразование через  string
	fmt.Println("Третья буква:", string(runes[2]))
}

-- где сам golang определят тип для рун как:

type rune = int32

-- т.е. как раз 4 байта для хранения символов юникода.

В примере выше мы посмотрели разобрали общее разбираение юникод-строки на символы и обращение к конкретному символу. Далее посмотрим на конкатенацию символов - как вообще "пересобирать" новые строки из старых.

Перебор символов строки - range()

В golang при использовании цикла for по строке range позволяет сразу перебирать именно руны (а не байты):


func main() {
	s := "Привет!"
	for i, r := range s {
		fmt.Printf("Позиция: %d, Символ: %c, Код: %d\n", i, r, r)
	}
}

-- тут используется форматированный вывод, если мы хотим использовать простую распечатку, то нужно будет приводить наши символы к строковому типу явно:

func main() {
	s := "Привет!"
	for i, r := range s {
		fmt.Println("Символ:", string(r), "Код:", i)
	}
}

Конкатенация рун - символов из строки

Для конкатенации символов можно использовать несколько способов:

import (
	"fmt"
	"strings"
)

func main() {
	s1 := "привет"
	s2 := "мир"

	// Преобразуем строки в срезы рун
	runes1 := []rune(s1)
	runes2 := []rune(s2)

	// Берём третий символ из каждой строки (индекс 2)
	char1 := runes1[2] // 'и'
	char2 := runes2[2] // 'р'

	// Далее вариаты конкатенации

	// Вариант 1 - литеральное объявление среза
	// Создаём срез рун из двух символов
	combinedRunes := []rune{char1, char2}
	strValue := string(combinedRunes)
	fmt.Println("Итог:", strValue)

	// Вариант 2-  конкатенация через билдер

	var builder strings.Builder
	builder.WriteRune(runes1[2]) // Добавляем первый символ
	builder.WriteRune(runes2[2]) // Добавляем второй символ
	// тут есть отдельный способ для получения строки:
	strValue = builder.String()
	fmt.Println("Итог:", strValue)

	// Вариант 3 - прямая конкатенация строк
	strValue = string(runes1[2]) + string(runes2[2])
	fmt.Println("Итог:", strValue)
}

Пример кода: Обмен, перестановка символов в строке

На тему данного урока хорошо ложится решение популярной алгоритмической задачи про разворот строки:

 
import "fmt"
 
func reverseString(s string) string {
    // Преобразуем строку в срез рун для корректной работы с Unicode
    runes := []rune(s)
    n := len(runes)
 
    // Меняем местами символы с начала и конца, двигаясь к центру
    for i := 0; i < n/2; i++ {
        runes[i], runes[n-1-i] = runes[n-1-i], runes[i]
    }
 
    return string(runes)
}
 
func main() {
    fmt.Println(reverseString("Привет!")) // Вывод: "olleh"
    fmt.Println(reverseString("Golang"))  // Вывод: "oG"
    fmt.Println(reverseString(""))        // Пустая строка: ""
}

-- тут мы используем цикл и офорляем логику решение в виде функции.