Byte e Rune
Go mette a disposizione due tipi per descrivere un carattere: byte
e rune. Entrambe questi tipi vengono rappresentati in memoria
come degli interi.
Byte
Il tipo byte viene rappresentato in memoria come un intero di 8
bit senza segno (uint8), i cui valori sono associati univocamente
a lettere secondo la tabella ASCII. I letterali byte sono
circondati da '.
La codifica ASCII
Lo standard ASCII risale al 1961 e contiene le indicazioni su come codificare lettere e simboli dell'alfabeto americano come interi. Ogni carattere occupa un byte. Poiché in inglese non sono presenti accenti nè altri simboli utilizzati in altre lingue, questa codifica è stata usata come base per un altro standard più versatile: UTF-8.
Rune
Il tipo rune rappresenta un code point della codifica UTF-8,
questo tipo è quello che useremo per la codifica dei caratteri nei
nostri sorgenti.
UTF-8 (Unicode)
La codifica UTF-8 utilizza una quantità variabile di bit per
codificare un range di 1.112.064 code points. I primi 128
caratteri sono quelli della tabella ASCII, gli altri caratteri
spaziano da lettere accentate fino ad emoticon. Per quanto capace di
rappresentare caratteri che necessitano di 4 byte (32 bit), i
caratteri ASCII utilizzano comunque 1 byte (8 bit).
Il pacchetto unicode
Il pacchetto unicode contiene molte funzioni utili per lavorare
con rune, per esempio:
func IsLetter(r rune) bool
func IsDigit(r rune) bool
func IsSpace(r rune) bool
func ToLower(r rune) rune
Stringhe
In Go una stringa è una sequenza di rune, cioè una sequenza di
caratteri di grandezza variabile. Le stringhe sono un tipo
immutabile, una volta create non possono essere modificate (capiremo
più avanti perchè).
Ci sono 2 tipi di letterali stringa:
- Interpretato: Circondato da
"", le sequenze di escape vengono interpretate (per esempio\nrappresenta un 'a capo',\trappresenta un tab,\"rappresenta letteralmente un ") - Raw: Circondato da ``, vengono prese letteralmente
Per esempio:
str := "Questa è la prima linea\nQuesta è la seconda linea"
str2 := ``Questa è la prima linea
Questa è la seconda``
Il valore di default di una stringa è "".
La funzione len()
La lunghezza di una stringa str si ottiene con la funzione
len:
len(str)
Concatenare stringhe
Due stringhe s1 ed s2 possono venire concatenate in un'unica
stringa utilizzando +
s := s1 + s2
For-range
Go mette a disposizione un quarto tipo di loop che agisce sulle
stringhe di rune. Vista la natura variabile in spazio dei
caratteri UTF-8 non è possibile trattarli con un semplice for
ternario. La sintassi è la seguente:
for pos, byte := range str {
// pos contiene l'indice attuale, byte la runa attuale
}
Con questo costrutto è possibile iterare sui code points uno alla
volta, byte conterrà una copia della runa attuale (è utile solo
per leggere valori, non può essere usato per cambiare la stringa).
Strconv e strings
I pacchetti strconv e strings contengono molte funzioni utili per lavorare sulle stringhe.
Esempi
strconv.Atoi
func Atoi(s string) (int, error)
Ritorna come primo valore l'intero corrispondente alla stringa s:
package main
import (
"fmt"
"strconv"
)
func main() {
var str string
var n, m int
n = 50
str = "45"
m, _ = strconv.Atoi(str)
fmt.Println(m + n) // Stampa 95
return
}
La funzione Itoa fa l'opposto:
func Itoa(i int) string
strings.HasPrefix, strings.HasSuffix
func HasPrefix(s, prefix string) bool
func HasSuffix(s, suffix string) bool
Queste funzioni testano se la stringa s contiene come
suffisso/prefisso la stringa prefix o suffix. Siccome
ritornano un bool possono essere usate in if e for.
strings.Contains
func Contains(s, substr string) bool
La funzione strings.Contains controlla se la stringa s
contiene la sottostringa substr.
E molte altre...
Esercizi
Nella risoluzione di questi esercizi mi aspetto 2 cose:
- Utilizzate
go docper trovare funzioni che potrebbero servirvi nei pacchettiunicode,stringsestrconv - Cercate di utilizzare delle funzioni per rendere il codice dentro
main()più leggibile. Ad esempio nell'esercizio 2 potreste servirvi di una funzionefunc isVocale(r rune) boole di una funzionefunc contaVocali(s string) int
Esercizio 0
Problema: Scrivere un programma es0.go che legge un byte (Occorre uno Scan in più per catturare l'invio) e
- Stampa il byte precedente, il byte stesso e quello successivo in ordine lessicografico (ASCII)
- Stabilisce se è una lettera tra A e L (maiuscole), o altro
Nota: Siccome i byte vengono rappresentati come interi in
memoria, b può anche essere scritto come
'a' + 1
Esempio di esecuzione:
Inserire un carattere: n
mno
altro
Inserire un carattere: C
BCD
A-L
Soluzione
package main
import "fmt"
func main() {
var c rune
fmt.Print("Inserire un carattere: ")
fmt.Scanf("%c", &c)
fmt.Printf("%c%c%c", c - 1, c, c + 1)
if 'A' < c && c < 'L' {
fmt.Println("A-L")
}
return
}
Esercizio 1 - Rompi stringa
Problema: Scrivere un programma rompi_stringa.go che legge in input una stringa e la stampa in verticale, una runa per riga.
Esempio di esecuzione:
Inserire una stringa: Perchè
P
e
r
c
h
è
Soluzione
package main
import "fmt"
func main() {
var s string
fmt.Print("Inserire una stringa: ")
fmt.Scan(&s)
rompi(s)
return
}
func rompi(s string) {
for _, c := range s {
fmt.Println(c)
}
}
Esercizio 2 - Conta vocali
Problema: Scrivere un programma conta_vocali.go che legge in
input una parola e stampa il numero di lettere (rune) che sono
vocali (aeiou).
Esempio di esecuzione:
Inserire una parola: Golang
2
Inserire una parola: supercalifragilisticoespiralidoso
15
Soluzione
package main
import (
"fmt"
"strings"
)
func main() {
var s string
fmt.Print("Inserire una parola: ")
fmt.Scan(&s)
fmt.Println(contaVocali(s))
return
}
func contaVocali(s string) int {
var count int
count += strings.Count(s, "a")
count += strings.Count(s, "e")
count += strings.Count(s, "i")
count += strings.Count(s, "o")
count += strings.Count(s, "u")
count += strings.Count(s, "A")
count += strings.Count(s, "E")
count += strings.Count(s, "I")
count += strings.Count(s, "O")
count += strings.Count(s, "U")
return count
}
Esercizio 3 - Trova rune
Problema: Scrivere un programma trova.go che legge un rune e
una stringa e stampa la prima posizione (indicizzata a partire da 0)
del carattere nella stringa, o -1 se il carattere non c'è.
Esempio di esecuzione:
Carattere da cercare: c
Stringa: Tre tigri contro tre tigri
10
Soluzione
package main
import (
"fmt"
"strings"
)
func main() {
var s string
var c rune
fmt.Print("Carattere da cercare: ")
fmt.Scanf("%c", &c)
fmt.Print("Stringa: ")
fmt.Scan(&s)
fmt.Println(strings.IndexRune(s, c))
return
}
Esercizio 4 - Minuscole o maiuscole
Problema: Scrivere un programma minuscole_maiuscole.go che legge una stringa e stabilisce se questa contiene solo maiuscole, solo minuscole o entrambe e stampa un messaggio opportuno.
Nota: Il pacchetto unicode contiene le funzioni IsLower e
IsUpper che potrebbero essere utili
Esempio di esecuzione:
Inserire una stringa: ATTENZIONE
Solo maiuscole
Inserire una stringa: Una frase
Sia maiuscole che minuscole
Inserire una stringa: dopo un punto fermo occorre una maiuscola
Solo minuscole
Soluzione
package main
import (
"fmt"
"unicode"
)
func main() {
var s string
fmt.Print("Inserire una stringa: ")
fmt.Scan(&s)
maiuscole, minuscole := false, false
for _, c := range {
if unicode.IsUpper(c) {
maiuscole = true
} else if unicode.IsLower(c) {
minuscole = true
}
}
if minuscole && maiuscole {
fmt.Println("Sia maiuscole che minuscole")
} else if maiuscole {
fmt.Println("Solo maiuscole")
} else if minuscole {
fmt.Println("Solo minuscole")
}
return
}
Esercizio 5 - Cesare
Problema: Scrivere un programma cesare.go che legge un valore
int non negativo k (la chiave di cifratura) e una sequenza di
lettere minuscole (sulla stessa riga e senza spazi), terminate da
'\n'. Il programma stampa la sequenza letta cifrata secondo il
cifrario di Cesare, usando come chiave k: ogni lettera del testo
viene sostituita dalla lettera che si trova k posizioni dopo
nell'alfabeto, ritornando dopo la 'z' alla lettera 'a'.
Nota: Ricordate che i byte sono semplicemente numeri. Quindi
per rimanere entro un certo range si può usare l'operazione '%': il
resto della divisione intera per n sta tra 0 e n - 1 e la
lunghezza dell'alfabeto è 'z' - 'a' + 1.
Esempio di esecuzione:
chiave: 2
testo: zaprb
bcrtd
chiave: 100
testo: abcd
wxyz
Soluzione
Vedi prossima lezione
