Skip to content

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

go
"Questa è una stringa normale\n"
"abcdefghijlmn\nopqrst\t\\uvwxyz"
Questa è una stringa normale
abcdefghijlmn
opqrst  \uvwxyz

Stringhe 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.

go
`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"

        Fine

Accesso

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

go
func main() {
   str := "this is a string"
   fmt.Println(str[0])
}

L'output è un valore di codifica byte, non un carattere

116

Tagliare una stringa

go
func main() {
   str := "this is a string"
   fmt.Println(string(str[0:4]))
}
this

Tentare di modificare un elemento di una stringa

go
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

go
func main() {
   str := "this is a string"
   str = "that is a string"
   fmt.Println(str)
}
that is a string

Conversione

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:

go
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.

go
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.

go
func main() {
  s1 := "hello world"
  b1 := unsafe.Slice(unsafe.StringData(s1), len(s1))
  fmt.Printf("%p %p", unsafe.StringData(s1), unsafe.SliceData(b1))
}
0xe27bb2 0xe27bb2

Lunghezza

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:

go
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 21

Sembra 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:

go
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 cinese

Copia

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

go
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

go
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 +

go
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

go
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

go
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 int

Iterazione

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

go
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

go
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

go
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

go
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

go
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.

Golang by www.golangdev.cn edit