unicode/utf8.RuneCountInString în Go

Numără caracterele (rune) dintr-un text UTF-8; len() numără octeții, iar ă, î, ș, ț ocupă câte 2 octeți.

Sintaxă
func RuneCountInString(s string) (n int)
func RuneCount(p []byte) int

Ce face unicode/utf8.RuneCountInString

În Go, un string este o secvență de octeți în codarea UTF-8. len(s) returnează numărul de octeți, nu de caractere. Literele englezești ocupă câte un octet, dar ă, â, î, ș, ț ocupă câte 2, € ocupă 3, iar emoji 4. Așa că len("Iași") este 5, deși cuvântul are 4 litere.

utf8.RuneCountInString(s) numără caracterele (în Go se numesc rune, adică puncte de cod Unicode). utf8.RuneCount(p) face același lucru pentru un []byte. Octeții invalizi sunt numărați fiecare ca un caracter.

Același lucru contează la indexare: s[0] este un octet (byte), nu o literă. Pentru caractere parcurge cu for i, r := range s (unde i este poziția în octeți, iar r caracterul) sau transformă în []rune(s), unde fiecare element este un caracter.

Parametri

Parametru Descriere
s / p Textul (string) sau octeții ([]byte).

Ce returnează

Numărul de caractere (int).

Exemple

Exemplul 1: len() numără octeți, RuneCountInString numără caractere

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	for _, s := range []string{"Cluj", "Iași", "Târgu Mureș", "10€", "țânțăroi"} {
		fmt.Printf("%-12s len = %2d   caractere = %2d\n", s, len(s), utf8.RuneCountInString(s))
	}
	fmt.Println(utf8.RuneCount([]byte("Brașov")))
	fmt.Println(len([]rune("Brașov")))
}

Rezultat

Cluj         len =  4   caractere =  4
Iași         len =  5   caractere =  4
Târgu Mureș  len = 13   caractere = 11
10€          len =  5   caractere =  3
țânțăroi     len = 12   caractere =  8
6
6

Exemplul 2: Octeți față de caractere la parcurgere

s[i] dă octeți: ș apare ca doi octeți (200 153). for range dă caracterele și poziția lor în octeți: după ș, poziția sare de la 2 la 4.

package main

import "fmt"

func main() {
	s := "Iași"
	for i := 0; i < len(s); i++ {
		fmt.Print(s[i], " ")
	}
	fmt.Println()
	for i, r := range s {
		fmt.Printf("poziția %d: %c\n", i, r)
	}
	litere := []rune(s)
	fmt.Println("a treia literă:", string(litere[2]), "| s[2] este octetul", s[2])
}

Rezultat

73 97 200 153 105 
poziția 0: I
poziția 1: a
poziția 2: ș
poziția 4: i
a treia literă: ș | s[2] este octetul 200

Exemplul 3: Scurtarea unui text la un număr de caractere

Tăierea cu s[:n] lucrează pe octeți și poate rupe o literă în două. Varianta corectă numără caracterele.

package main

import (
	"fmt"
	"unicode/utf8"
)

func scurteaza(s string, max int) string {
	if utf8.RuneCountInString(s) <= max {
		return s
	}
	return string([]rune(s)[:max]) + "…"
}

func main() {
	titlu := "Știri din Iași și Brașov"
	fmt.Printf("%q\n", titlu[:3])
	fmt.Println(scurteaza(titlu, 9))
	fmt.Println(scurteaza("Cluj", 9))
}

Rezultat

"Șt"
Știri din…
Cluj

Greșeli frecvente

  • Folosești len(s) ca număr de litere (de exemplu „parola trebuie să aibă minim 8 caractere”). „țânțăroi” are 8 litere, dar 12 octeți; folosește utf8.RuneCountInString.
  • Tai textul cu s[:n] și obții o literă ruptă (afișată ca �). Taie după []rune(s) sau verifică granițele cu utf8.RuneStart.
  • Crezi că un rune este mereu o literă „văzută”. Unele litere pot fi scrise din două puncte de cod (literă + semn diacritic combinat); pentru texte venite din surse diferite, normalizează-le cu golang.org/x/text/unicode/norm.

Comenzi înrudite

  • unicode/utf8.DecodeRuneInString: Citește primul (sau ultimul) caracter UTF-8 dintr-un text sau []byte și spune câți octeți ocupă.
  • unicode/utf8.EncodeRune: EncodeRune scrie un caracter în UTF-8 într-un []byte, AppendRune îl adaugă la final, iar RuneLen spune câți octeți ocupă.
  • unicode/utf8.ValidString: Verifică dacă un text sau un []byte este UTF-8 valid și dacă o valoare rune este un caracter Unicode valid.
  • strings.Index: Returnează poziția (în octeți) a primei sau ultimei apariții a unui text, octet sau caracter; -1 dacă nu există.
  • strings.Count: Numără de câte ori apare un text în alt text, fără suprapuneri.