Функция len golang символами UTF-8

package main
import "fmt"
// import "unsafe"

func main() {
    word := string("крен")
    // lastSymb := string(word[len(word)-1])
    // fmt.Println(lastSymb)
    // fmt.Println(word)
    // fmt.Println(unsafe.Sizeof(word))
    fmt.Println(len(word))
}

Началось с того, что я не мог взять последний символ у русского слова, оказывается, многие источники неверно трактуют возвращаемое значение функции len() ,видимо переводят с английского,в котором все буквы попадают в классическую ASCII.ВНИМАНИЕ функция возвращает длину строки в БАЙТАХ, а не в символах. Вопросы такие:

  1. Почему возвращает 8 для слова крен, а не 10, где же символ конца строки?
  2. Какой есть элегантный способ получать длину ЛЮБОЙ строки в символах?

UPD Нашел с использованием либы "unicode/utf8" , но неужели мне надо притягивать целую либу просто для такого простого действия???Есть другие варианты?


Ответы (1 шт):

Автор решения: Pak Uula

len возвращает число элементов в массиве или мапе. Тип string по определению это массив байтов фиксированной длины. Поэтому len(word) возвращает 8 байтов - ровно столько занимает представление слова крен в utf-8. В go строки НЕ zero-terminated, поэтому len возвращает 8, а не 9.

Буквы в go представляются типом rune. Для string определено специальное приведение типов []rune(), которое возвращает массив букв строки (32-х битных символов UCS -2)

Приведение к массиву букв включает разбор байтовых последовательностей и запись 4х байтовых рун в память. Функция utf8.RuneCountInString подсчитывает юникодные символы без записи в память. Поэтому, если вам нужно только число символов, эта функция будет быстрее, чем len([]rune(txt)).

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    txt := "крен"
    fmt.Println("len:", len(txt))
    fmt.Println("len([]rune):", len([]rune(txt)))
    fmt.Println("Rune count", utf8.RuneCountInString(txt))
}

https://go.dev/play/p/mYbjJpMeRDz

UPD

Последний символ можно добыть двумя способами - полным перекодированием в массив юникодных символов (ака []rune) и декодированием только последнего символа

    // первый способ - конвертировать в последовательность юникодных символов
    runes := []rune(txt)
    lastRune := runes[len(runes)-1]
    fmt.Println("последняя буква: ", string(lastRune))
    // второй способ - читать байты с конца, пока не получится валидный utf-8
    decodedLastRune, _ := utf8.DecodeLastRuneInString(txt)
    fmt.Println("последняя буква: ", string(decodedLastRune))

https://go.dev/play/p/e071vJRW-gk

→ Ссылка