O tipo rune em Go é um alias de int32.
Dado este tipo subjacente int32, o tipo rune contém um valor inteiro de 32 bits com sinal.
No entanto, ao contrário do tipo int32, o valor inteiro guardado num tipo rune representa um único caráter Unicode.
O Unicode é um superconjunto do ASCII que representa carateres atribuindo um número único a cada caráter. A esse número único dá-se o nome de ponto de código Unicode. O Unicode tem como objetivo representar todos os carateres do mundo, incluindo vários alfabetos, números, símbolos e até emoji, como pontos de código Unicode.
Em Go, o tipo rune representa um único ponto de código Unicode.
A tabela seguinte contém exemplos de carateres Unicode, juntamente com o respetivo ponto de código Unicode e valor decimal:
| Caráter Unicode | Ponto de código Unicode | Valor decimal |
|---|---|---|
| 0 | U+0030 |
48 |
| A | U+0041 |
65 |
| a | U+0061 |
97 |
| ¿ | U+00BF |
191 |
| π | U+03C0 |
960 |
| 🧠 | U+1F9E0 |
129504 |
O UTF-8 é uma codificação de carateres de largura variável que é usada para codificar cada ponto de código Unicode em 1, 2, 3 ou 4 bytes.
Como um ponto de código Unicode pode ser codificado no máximo em 4 bytes, o tipo rune tem de ser capaz de conter até 4 bytes de dados.
É por isso que o tipo rune é um alias de int32: um tipo int32 é capaz de conter até 4 bytes de dados.
Os ficheiros de código-fonte de Go são codificados em UTF-8.
As variáveis do tipo rune declaram-se colocando um caráter entre plicas:
myRune := '¿'
Como rune é apenas um alias de int32, imprimir o tipo de um rune dá int32:
myRune := '¿'
fmt.Printf("myRune type: %T\n", myRune)
// Output: myRune type: int32
Da mesma forma, imprimir o valor de um rune dá o seu valor inteiro (decimal):
myRune := '¿'
fmt.Printf("myRune value: %v\n", myRune)
// Output: myRune value: 191
Para imprimir o caráter Unicode representado pelo rune, usa o verbo de formatação %c:
myRune := '¿'
fmt.Printf("myRune Unicode character: %c\n", myRune)
// Output: myRune Unicode character: ¿
Para imprimir o ponto de código Unicode representado pelo rune, usa o verbo de formatação %U:
myRune := '¿'
fmt.Printf("myRune Unicode code point: %U\n", myRune)
// Output: myRune Unicode code point: U+00BF
As strings em Go são codificadas em UTF-8, o que significa que contêm carateres Unicode. Os carateres dentro das strings são guardados e codificados em 1, 2, 3 ou 4 bytes, dependendo do caráter Unicode que representam.
Em Go, usam-se slices para representar sequências, e essas slices podem ser percorridas com range.
Quando percorremos uma string, o Go converte-a numa série de runes, cada um com 4 bytes (lembra-te: o tipo rune é um alias de um int32!)
Embora uma string seja apenas um slice de bytes, a palavra-chave range itera sobre os runes de uma string, e não sobre os seus bytes.
Neste exemplo, a variável index representa o índice inicial da sequência de bytes do rune atual, e a variável char representa o rune atual:
myString := "❗hello"
for index, char := range myString {
fmt.Printf("Index: %d\tCharacter: %c\t\tCode Point: %U\n", index, char, char)
}
// Output:
// Index: 0 Character: ❗ Code Point: U+2757
// Index: 3 Character: h Code Point: U+0068
// Index: 4 Character: e Code Point: U+0065
// Index: 5 Character: l Code Point: U+006C
// Index: 6 Character: l Code Point: U+006C
// Index: 7 Character: o Code Point: U+006F
Como os runes podem ser guardados em 1, 2, 3 ou 4 bytes, o comprimento de uma string pode nem sempre ser igual ao número de carateres dessa string.
Usa a função incorporada len para obter o comprimento de uma string em bytes e a função utf8.RuneCountInString para obter o número de runes de uma string:
import "unicode/utf8"
myString := "❗hello"
stringLength := len(myString)
numberOfRunes := utf8.RuneCountInString(myString)
fmt.Printf("myString - Length: %d - Runes: %d\n", stringLength, numberOfRunes)
// Output: myString - Length: 8 - Runes: 6
Foi-te pedido que criasses uma biblioteca de logs para ajudar a gerir os logs da tua organização. Esta biblioteca vai permitir aos utilizadores identificar que aplicação emitiu um determinado log, corrigir logs corrompidos e determinar se uma linha de log está dentro de um certo limite de carateres.
Os logs vêm de várias aplicações, cada uma com o seu próprio formato de log proprietário. É preciso identificar a aplicação que emitiu um log antes de o poder guardar num sistema de agregação de logs.
Implementa a função Application, que recebe uma linha de log e devolve a aplicação que emitiu essa linha de log.
Para identificar que aplicação emitiu uma determinada linha de log, procura nessa linha um caráter específico, como mostra a tabela seguinte:
| Aplicação | Caráter | Ponto de código Unicode |
|---|---|---|
recommendation |
❗ | U+2757 |
search |
🔍 | U+1F50D |
weather |
☀ | U+2600 |
Se uma linha de log não contiver nenhum dos carateres da tabela acima, devolve default a quem chamou a função.
Se uma linha de log contiver mais do que um caráter da tabela acima, devolve a aplicação correspondente ao primeiro caráter encontrado na linha, da esquerda para a direita.
Application("❗ recommended search product 🔍")
// => recommendation
Devido a um bug raro mas persistente na infraestrutura de logging, certos carateres nos logs podem ficar corrompidos. Depois de passares algum tempo a identificar os carateres corrompidos e o seu valor original, decides atualizar a biblioteca de logs para ajudar a corrigir logs corrompidos.
Implementa a função Replace, que recebe uma linha de log, um caráter corrompido e o valor original, e devolve uma linha de log modificada em que todas as ocorrências do caráter corrompido são substituídas pelo valor original.
log := "please replace '👎' with '👍'"
Replace(log, '👎', '👍')
// => please replace '👍' with '👍'"
Os sistemas responsáveis por apresentar logs têm um limite no número de carateres que podem ser apresentados por linha de log. Por isso, os utilizadores estão a pedir que esta biblioteca inclua uma função auxiliar para determinar se uma linha de log está dentro de um limite de carateres específico.
Implementa a função WithinLimit, que recebe uma linha de log e um limite de carateres e devolve se a linha está ou não dentro desse limite.
WithinLimit("hello❗", 6)
// => true
Inscreve-te no Exercism para aprenderes e dominares Go com 34 conceitos165 exercícios, e mentoria humana real, tudo grátis.