Percursos
/
Go
Go
/
Exercícios
/
Registos, registos, registos!
Registos, registos, registos!

Registos, registos, registos!

Exercício de aprendizagem

Introdução

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.

Unicode e pontos de código

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

UTF-8

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.

Como usar runes

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

Runes e strings

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

Instruções

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.

1. Identifica que aplicação emitiu um log

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

2. Corrige logs corrompidos

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 '👍'"

3. Determina se um log pode ser apresentado

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
Editar via GitHub A ligação abre numa nova janela ou separador
Go Exercism

Estás pronto para começar Registos, registos, registos!?

Inscreve-te no Exercism para aprenderes e dominares Go com 34 conceitos165 exercícios, e mentoria humana real, tudo grátis.