Strings, bytes e runes
Nesta aula, exploramos como Go lida com strings, bytes e runes, enfatizando a codificação UTF-8. Aprendemos a iterar sobre strings com range, usar o pacote strings para manipulação e o pacote strconv para conversões.
Em Go, strings são sequências imutáveis de bytes, mas a representação de texto Unicode é feita com runes (códigos de ponto Unicode). Esta aula aborda os fundamentos de UTF-8, iteração sobre strings, e os pacotes da biblioteca padrão para manipulação e conversão.
Entender a diferença entre bytes e runes é crucial para processar texto corretamente em Go, especialmente com caracteres multi-byte. Vamos ver como Go lida com codificação UTF-8 de forma nativa e eficiente.
UTF-8 em Go
Go adota UTF-8 como codificação padrão para strings. Uma string em Go é uma fatia de bytes ([]byte) que pode conter qualquer sequência de bytes, mas quando representa texto, espera-se que seja UTF-8 válido. Uma rune é um valor inteiro de 32 bits que representa um ponto de código Unicode.
Para acessar caracteres individuais, é necessário converter a string para uma fatia de runes ou usar o loop range. O tipo rune é um alias para int32 e é usado para armazenar valores Unicode.
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
s := "Olá, 世界"
fmt.Println(len(s)) // bytes: 13
fmt.Println(utf8.RuneCountInString(s)) // runes: 8
for i, r := range s {
fmt.Printf("%d: %c (rune %U)\n", i, r, r)
}
}No exemplo, len(s) retorna o número de bytes (13), enquanto utf8.RuneCountInString retorna 8 runes. O loop range itera sobre runes, não bytes, e retorna o índice inicial de cada rune.
range em strings
O loop for range em strings itera sobre runes, decodificando UTF-8 automaticamente. A cada iteração, ele retorna o índice de byte inicial e a rune correspondente. Se a string contiver bytes inválidos, a rune é \uFFFD (caractere de substituição).
Para iterar sobre bytes, use um loop tradicional com índice. Para obter uma fatia de runes, use []rune(s).
package main
import "fmt"
func main() {
s := "Ação"
for i, r := range s {
fmt.Printf("byte %d: rune %c (U+%04X)\n", i, r, r)
}
// byte 0: A (U+0041)
// byte 1: ç (U+00E7)
// byte 3: ã (U+00E3)
// byte 5: o (U+006F)
// Iterando sobre bytes:
for i := 0; i < len(s); i++ {
fmt.Printf("byte %d: %02x\n", i, s[i])
}
}Note que a rune 'ç' ocupa dois bytes (c3 a7) e 'ã' ocupa dois bytes (c3 a3). O range pula para o próximo índice após cada rune.
Pacote strings
O pacote strings oferece funções para manipular strings UTF-8. As principais incluem:
Contains,Count,HasPrefix,HasSuffixIndex,LastIndex(buscam substrings)Join,Split,Replace,ToUpper,ToLowerTrim,TrimSpace
Essas funções operam em runes, não bytes, respeitando caracteres multi-byte.
package main
import (
"fmt"
"strings"
)
func main() {
s := " Olá, Mundo! "
fmt.Println(strings.TrimSpace(s)) // "Olá, Mundo!"
fmt.Println(strings.ToUpper(s)) // " OLÁ, MUNDO! "
fmt.Println(strings.Replace(s, "Mundo", "Go", 1)) // " Olá, Go! "
fmt.Println(strings.Split("a,b,c", ",")) // ["a" "b" "c"]
}Pacote strconv
O pacote strconv converte strings para tipos básicos e vice-versa. Funções importantes:
AtoieItoapara inteirosParseInt,ParseFloat,ParseBoolFormatInt,FormatFloat,FormatBoolQuoteeUnquotepara strings com escape
Essas funções lidam com representações textuais de dados.
package main
import (
"fmt"
"strconv"
)
func main() {
i, err := strconv.Atoi("42")
if err != nil {
panic(err)
}
fmt.Println(i + 1) // 43
s := strconv.Itoa(123)
fmt.Println(s) // "123"
f, _ := strconv.ParseFloat("3.14", 64)
fmt.Println(f) // 3.14
q := strconv.Quote("Hello, \"世界\"")
fmt.Println(q) // "\"Hello, \\"世界\\"\""
}Referências
- Go Blog: Strings, bytes, runes and characters in Go
- Package strings - Go Documentation
- Package strconv - Go Documentation
- Go Language Specification: Rune literals
- Wikipedia: UTF-8
Exercícios
Crie uma função que receba uma string e retorne o número de runes (caracteres Unicode) nela.
✓ Resposta:func countRunes(s string) int { return utf8.RuneCountInString(s) }Escreva um programa que use range para imprimir cada rune de uma string com seu índice de byte e valor Unicode.
✓ Resposta:package main import "fmt" func main() { s := "Go é show!" for i, r := range s { fmt.Printf("byte %d: %c (U+%04X)\n", i, r, r) } }Use o pacote strings para verificar se uma string contém a substring "Go" e, se sim, substitua por "Python".
✓ Resposta:package main import ( "fmt" "strings" ) func main() { s := "I love Go!" if strings.Contains(s, "Go") { s = strings.Replace(s, "Go", "Python", 1) } fmt.Println(s) }Converta a string "123" para inteiro usando strconv, adicione 10 e imprima o resultado.
✓ Resposta:package main import ( "fmt" "strconv" ) func main() { n, _ := strconv.Atoi("123") fmt.Println(n + 10) }Escreva uma função que receba uma string com espaços extras e retorne uma versão com espaços simples e trimada.
✓ Resposta:func normalizeSpaces(s string) string { fields := strings.Fields(s) return strings.Join(fields, " ") }