Go by Example: Strings and Runes

Un string en Go es una porción de bytes de solo lectura. El lenguaje y la biblioteca estándar tratan las cadenas (strings) de manera especial - como contenedores de texto codificado en UTF-8. En otros lenguajes, las cadenas (strings) están hechas de “caracteres”. En Go, el concepto de un carácter se llama rune - es un entero que representa un punto de código Unicode. Esta publicación del blog de Go es una buena introducción al tema.

package main
import (
    "fmt"
    "unicode/utf8"
)
func main() {

s es una string asignada con un valor literal que representa la palabra “hello” en el idioma tailandés. Los literales de cadena de Go son texto codificado en UTF-8.

    const s = "สวัสดี"

Ya que las cadenas son equivalentes a []byte, esto producirá la longitud de los bytes crudos almacenados dentro.

    fmt.Println("Len:", len(s))

Indexar en una cadena produce los valores de byte crudo en cada índice. Este bucle genera los valores hexadecimales de todos los bytes que constituyen los puntos de código en s.

    for i := 0; i < len(s); i++ {
        fmt.Printf("%x ", s[i])
    }
    fmt.Println()

Para contar cuántos runes hay en una cadena, podemos usar el paquete utf8. Note que el tiempo de ejecución de RuneCountInString depende del tamaño de la cadena, porque tiene que decodificar cada rune UTF-8 secuencialmente. Algunos caracteres tailandeses están representados por múltiples puntos de código UTF-8, por lo que el resultado de este conteo puede ser sorprendente.

    fmt.Println("Rune count:", utf8.RuneCountInString(s))

Un bucle range maneja las cadenas de manera especial y decodifica cada rune junto con su desplazamiento en la cadena.

    for idx, runeValue := range s {
        fmt.Printf("%#U starts at %d\n", runeValue, idx)
    }

Podemos lograr la misma iteración usando explícitamente la función utf8.DecodeRuneInString.

    fmt.Println("\nUsing DecodeRuneInString")
    for i, w := 0, 0; i < len(s); i += w {
        runeValue, width := utf8.DecodeRuneInString(s[i:])
        fmt.Printf("%#U starts at %d\n", runeValue, i)
        w = width

Esto demuestra pasar un valor rune a una función.

        examineRune(runeValue)
    }
}
func examineRune(r rune) {

Los valores encerrados en comillas simples son literales de rune. Podemos comparar un valor rune directamente con un literal de rune.

    if r == 't' {
        fmt.Println("found tee")
    } else if r == 'ส' {
        fmt.Println("found so sua")
    }
}
$ go run strings-and-runes.go
Len: 18
e0 b8 aa e0 b8 a7 e0 b8 b1 e0 b8 aa e0 b8 94 e0 b8 b5 
Rune count: 6
U+0E2A 'ส' starts at 0
U+0E27 'ว' starts at 3
U+0E31 'ั' starts at 6
U+0E2A 'ส' starts at 9
U+0E14 'ด' starts at 12
U+0E35 'ี' starts at 15
Using DecodeRuneInString
U+0E2A 'ส' starts at 0
found so sua
U+0E27 'ว' starts at 3
U+0E31 'ั' starts at 6
U+0E2A 'ส' starts at 9
found so sua
U+0E14 'ด' starts at 12
U+0E35 'ี' starts at 15

Siguiente ejemplo: Structs.