string-uri, rune și byte în Go

Textul în Go este UTF-8: len dă octeți, range dă rune (caractere), byte este un octet, rune un caracter.

Sintaxă
s := "Iași"           // string: octeți UTF-8, nemodificabil
r := 'ș'               // rune (int32): un caracter Unicode
var b byte = s[0]      // byte (uint8): un octet
[]rune(s)  []byte(s)  string(r)

Ce face string-uri, rune și byte

Un string este o secvență de octeți, nemodificabilă, care de obicei conține text UTF-8. len(s) dă numărul de octeți, nu de litere: „ș” ocupă 2 octeți, deci len("Iași") este 5. s[i] dă un byte (alias pentru uint8), adică un octet, nu o literă.

Un rune (alias pentru int32) este un caracter Unicode și se scrie între apostroafe: 'ș'. for i, r := range s decodează textul și dă literele una câte una. utf8.RuneCountInString(s) numără literele, []rune(s) transformă textul într-un slice de caractere, iar []byte(s) într-un slice de octeți.

Textele nu se pot modifica pe loc (s[0] = 'X' nu compilează). Construiești unul nou: cu +, cu strings.Builder pentru multe bucăți, sau prin []rune. Literalele între apostroafe inverse (`C:\cale\n`) sunt texte brute, fără secvențe \n, și pot avea mai multe rânduri.

Exemple

Exemplul 1: Octeți și litere

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    oras := "Timișoara"
    fmt.Println("octeți:", len(oras))
    fmt.Println("litere:", utf8.RuneCountInString(oras))
    fmt.Println("primul octet:", oras[0], string(oras[0]))

    for i, r := range "ăîș" {
        fmt.Printf("poziția %d: %c (cod %d, %d octeți)\n", i, r, r, utf8.RuneLen(r))
    }
}

Rezultat

octeți: 10
litere: 9
primul octet: 84 T
poziția 0: ă (cod 259, 2 octeți)
poziția 2: î (cod 238, 2 octeți)
poziția 4: ș (cod 537, 2 octeți)

Exemplul 2: Inversarea unui text cu diacritice

Prin []rune se inversează literele, nu octeții, deci diacriticele rămân corecte.

package main

import "fmt"

func inverseaza(s string) string {
    r := []rune(s)
    for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
        r[i], r[j] = r[j], r[i]
    }
    return string(r)
}

func main() {
    fmt.Println(inverseaza("Brașov"))
    fmt.Println(inverseaza("țară"))
    fmt.Println([]byte("ț"), []rune("ț"))
}

Rezultat

voșarB
ăraț
[200 155] [539]

Exemplul 3: Text brut și construirea textelor

package main

import (
    "fmt"
    "strings"
)

func main() {
    cale := `C:\proiecte\nou`
    fmt.Println(cale)

    var b strings.Builder
    for i, oras := range []string{"Iași", "Cluj", "Arad"} {
        if i > 0 {
            b.WriteString(", ")
        }
        b.WriteString(oras)
    }
    fmt.Println(b.String())

    s := "casa"
    s = "m" + s[1:] // texte noi, originalul nu se modifică
    fmt.Println(s)
}

Rezultat

C:\proiecte\nou
Iași, Cluj, Arad
masa

Greșeli frecvente

  • len(s) ca număr de litere: greșit pentru texte cu diacritice. Folosește utf8.RuneCountInString.
  • s[:3] pe un text cu diacritice poate tăia o literă în două și dă caractere invalide.
  • s[0] = 'X': cannot assign to s[0] (neither addressable nor a map index expression); textele sunt nemodificabile.
  • string(65) crezând că dă "65": dă "A". Folosește strconv.Itoa.

Comenzi înrudite

  • string: Tipul pentru texte: o secvență de octeți nemodificabilă, de obicei UTF-8; se concatenează cu + și se compară cu ==.
  • tipuri de bază: Tipurile simple din Go: bool, string, întregi cu și fără semn, numere reale, complexe, byte și rune.
  • range: Parcurge într-un for elementele unui slice, array, map, text, canal, număr sau iterator.
  • int, int8…int64, uint, uint8…uint64, uintptr: Tipurile întregi din Go: cu semn (int, int8..int64) și fără semn (uint, uint8..uint64, uintptr), cu limitele lor.