Stringhe Go
In Go, una stringa è essenzialmente una sequenza di byte immutabile e di sola lettura (byte sequence). Qui "sequenza di byte" si riferisce al fatto che i dati sottostanti di una stringa sono composti da una serie di byte disposti in ordine, questi byte occupano uno spazio di memoria continuo.
Letterali
Come menzionato in precedenza, ci sono due modi per esprimere i letterali di stringa: stringhe normali e stringhe native.
Stringhe normali
Le stringhe normali sono indicate da virgolette doppie "", supportano l'escape, non supportano la scrittura su più righe. Di seguito sono riportate alcune stringhe normali
"Questa è una stringa normale\n"
"abcdefghijlmn\nopqrst\t\\uvwxyz"Questa è una stringa normale
abcdefghijlmn
opqrst \uvwxyzStringhe native
Le stringhe native sono indicate da accenti gravi, non supportano l'escape, supportano la scrittura su più righe. Tutti i caratteri in una stringa nativa verranno outputati così come sono, inclusi ritorni a capo e indentazione.
`Questa è una stringa nativa, ritorno a capo
indentazione tab, \t carattere di tabulazione ma non valido, ritorno a capo
"Questa è una stringa normale"
Fine
`Questa è una stringa nativa, ritorno a capo
indentazione tab, \t carattere di tabulazione ma non valido, ritorno a capo
"Questa è una stringa normale"
FineAccesso
Poiché una stringa è essenzialmente una sequenza di byte, l'operazione di indice str[i] è progettata per restituire l'i-esimo byte. La sintassi è coerente con le slice. Ad esempio, per accedere al primo elemento di una stringa
func main() {
str := "this is a string"
fmt.Println(str[0])
}L'output è un valore di codifica byte, non un carattere
116Tagliare una stringa
func main() {
str := "this is a string"
fmt.Println(string(str[0:4]))
}thisTentare di modificare un elemento di una stringa
func main() {
str := "this is a string"
str[0] = 'a' // Non supera la compilazione
fmt.Println(str)
}main.go:7:2: cannot assign to str[0] (value of type byte)Sebbene non sia possibile modificare una stringa, è possibile sovrascriverla
func main() {
str := "this is a string"
str = "that is a string"
fmt.Println(str)
}that is a stringConversione
Le stringhe possono essere convertite in slice di byte e le slice di byte o sequenze di byte possono anche essere convertite in stringhe. Ecco un esempio:
func main() {
str := "this is a string"
// Conversione esplicita del tipo in slice di byte
bytes := []byte(str)
fmt.Println(bytes)
// Conversione esplicita del tipo in stringa
fmt.Println(string(bytes))
}Il contenuto di una stringa è di sola lettura e immutabile, non può essere modificato, ma una slice di byte può essere modificata.
func main() {
str := "this is a string"
fmt.Println(&str)
bytes := []byte(str)
// Modifica della slice di byte
bytes = append(bytes, 96, 97, 98, 99)
// Assegnazione alla stringa originale
str = string(bytes)
fmt.Println(str)
}Dopo aver convertito una stringa in una slice di byte, le due non hanno alcuna relazione, perché Go allocherà un nuovo spazio di memoria per la slice di byte e copierà la memoria della stringa. Modificare la slice di byte non avrà alcun effetto sulla stringa originale. Questo viene fatto per la sicurezza della memoria.
In questo caso, se la stringa o la slice di byte da convertire è grande, il costo delle prestazioni sarà elevato. Tuttavia, è anche possibile utilizzare la libreria unsafe per eseguire una conversione senza copia, ma i problemi di sicurezza sottostanti devono essere assunti da sé. Ad esempio, nell'esempio seguente, b1 e s1 hanno lo stesso indirizzo.
func main() {
s1 := "hello world"
b1 := unsafe.Slice(unsafe.StringData(s1), len(s1))
fmt.Printf("%p %p", unsafe.StringData(s1), unsafe.SliceData(b1))
}0xe27bb2 0xe27bb2Lunghezza
La lunghezza di una stringa, in realtà, non è il numero di caratteri, ma la lunghezza della sequenza di byte. Solo nella maggior parte dei casi gestiamo caratteri ASCII, ogni carattere può essere rappresentato esattamente da un byte, quindi la lunghezza in byte e il numero di caratteri coincidono恰好. Per ottenere la lunghezza di una stringa si utilizza la funzione incorporata len. Ecco un esempio:
func main() {
str := "this is a string" // Sembra che la lunghezza sia 16
str2 := "这是一个字符串" // Sembra che la lunghezza sia 7
fmt.Println(len(str), len(str2))
}16 21Sembra che la stringa di caratteri cinesi sia più corta della stringa di caratteri inglesi, ma la lunghezza effettiva ottenuta è più lunga della stringa di caratteri inglesi. Questo perché nella codifica unicode, un carattere cinese occupa nella maggior parte dei casi 3 byte, un carattere inglese occupa solo un byte. Outputtando il primo elemento della stringa si può vedere il risultato:
func main() {
str := "this is a string"
str2 := "这是一个字符串"
fmt.Println(string(str[0]))
fmt.Println(string(str2[0]))
fmt.Println(string(str2[0:3]))
}t // Lettera t
è // "Frammento" (primo byte) di un carattere cinese, che per coincidenza ha lo stesso valore di codifica del carattere italiano è
这 // Carattere cineseCopia
Simile al modo di copiare array e slice, la copia di stringhe è in realtà una copia di slice di byte. Utilizza la funzione incorporata copy
func main() {
var dst, src string
src = "this is a string"
desBytes := make([]byte, len(src))
copy(desBytes, src)
dst = string(desBytes)
fmt.Println(src, dst)
}Puoi anche utilizzare la funzione strings.Clone, ma in realtà l'implementazione interna è quasi la stessa
func main() {
var dst, src string
src = "this is a string"
dst = strings.Clone(src)
fmt.Println(src, dst)
}Concatenazione
La concatenazione di stringhe utilizza l'operatore +
func main() {
str := "this is a string"
str = str + " that is a int"
fmt.Println(str)
}Puoi anche convertire in slice di byte e poi aggiungere elementi
func main() {
str := "this is a string"
bytes := []byte(str)
bytes = append(bytes, "that is a int"...)
str = string(bytes)
fmt.Println(str)
}Entrambi i metodi di concatenazione precedenti hanno prestazioni scarse. In generale possono essere utilizzati, ma se ci sono requisiti di prestazioni più elevati, è possibile utilizzare strings.Builder
func main() {
builder := strings.Builder{}
builder.WriteString("this is a string ")
builder.WriteString("that is a int")
fmt.Println(builder.String())
}this is a string that is a intIterazione
All'inizio di questo articolo è già stato menzionato che una stringa in Go è una slice di byte di sola lettura, il che significa che l'unità di composizione di una stringa è byte, non caratteri. Questa situazione si incontra spesso durante l'iterazione di una stringa. Ad esempio, il codice seguente
func main() {
str := "hello world!"
for i := 0; i < len(str); i++ {
fmt.Printf("%d,%x,%s\n", str[i], str[i], string(str[i]))
}
}Nell'esempio vengono outputati rispettivamente la forma decimale e esadecimale del byte.
104,68,h
101,65,e
108,6c,l
108,6c,l
111,6f,o
32,20,
119,77,w
111,6f,o
114,72,r
108,6c,l
100,64,d
33,21,!Poiché i caratteri nell'esempio appartengono tutti ai caratteri ASCII, è sufficiente un byte per rappresentarli, quindi il risultato è che ogni byte corrisponde恰好 a un carattere. Ma se contiene caratteri non ASCII, il risultato è diverso. Di seguito
func main() {
str := "hello 世界!"
for i := 0; i < len(str); i++ {
fmt.Printf("%d,%x,%s\n", str[i], str[i], string(str[i]))
}
}In genere, un carattere cinese occupa 3 byte, quindi è possibile vedere il seguente risultato
104,68,h
101,65,e
108,6c,l
108,6c,l
111,6f,o
32,20,
228,e4,ä
184,b8,¸
150,96,
231,e7,ç
149,95,
140,8c,
33,21,!Iterare per byte dividerà i caratteri cinesi, il che ovviamente causerà caratteri garbati. Le stringhe in Go supportano esplicitamente UTF-8. Per gestire questa situazione è necessario utilizzare il tipo rune. Quando si utilizza for range per l'iterazione, l'unità di iterazione predefinita è un rune. Ad esempio, il codice seguente
func main() {
str := "hello 世界!"
for _, r := range str {
fmt.Printf("%d,%x,%s\n", r, r, string(r))
}
}Output come segue
104,68,h
101,65,e
108,6c,l
108,6c,l
111,6f,o
32,20,
19990,4e16,世
30028,754c,界
33,21,!rune è essenzialmente un alias del tipo int32. L'intervallo del set di caratteri unicode si trova tra 0x0000 - 0x10FFFF, al massimo solo tre byte. Il numero massimo di byte per una codifica UTF-8 valida è solo 4 byte, quindi è ovvio utilizzare int32 per memorizzare. Convertire prima la stringa in []rune e poi iterare è lo stesso principio. Di seguito
func main() {
str := "hello 世界!"
runes := []rune(str)
for i := 0; i < len(runes); i++ {
fmt.Println(string(runes[i]))
}
}È anche possibile utilizzare gli strumenti del pacchetto utf8. Ad esempio
func main() {
str := "hello 世界!"
for i, w := 0, 0; i < len(str); i += w {
r, width := utf8.DecodeRuneInString(str[i:])
fmt.Println(string(r))
w = width
}
}L'output di questi due esempi è lo stesso.
TIP
Per maggiori dettagli sulle stringhe, puoi visitare Strings, bytes, runes and characters in Go.
