Pular para o conteúdo principal

Strings

8 min de leitura•Arquivado emLinguagem de Programação Goem

Mergulho profundo em strings no Go — armazenadas como sequências de bytes UTF-8, imutáveis e com suporte completo a Unicode. Aprenda indexação, slicing, conversões e como iterar corretamente.

O que é uma string

Em Go, uma string é uma sequência de bytes somente leitura. Não é uma sequência de caracteres, não é uma sequência de code points Unicode — são bytes. A linguagem não garante o que esses bytes representam. Por convenção, e por padrão em todo código-fonte Go, espera-se que os dados de uma string sejam UTF-8 válido, mas o tipo em si não impõe isso.

Concretamente, uma string é uma estrutura de dois campos: um ponteiro para o array de bytes subjacente e um comprimento. É só isso. Sem null terminator, sem campo de capacidade — apenas um ponteiro e uma contagem.

String literals são escritas com aspas duplas. Go também suporta raw string literals delimitados por backticks — eles abrangem múltiplas linhas e ignoram escape sequences:

Indexação e slicing

Como uma string é uma sequência de bytes, a notação de índice retorna um byte — não um caractere:

Você pode extrair uma substring usando uma slice expression. A sintaxe é a mesma que para slices: s[low:high] retorna os bytes do índice low até (mas não incluindo) o índice high:

O resultado ainda é uma string — o slicing não copia os bytes subjacentes. A nova string compartilha memória com a original.

Strings são imutáveis

Uma vez criada, uma string não pode ser modificada. Você pode reatribuir a variável, mas não pode alterar os bytes para os quais a string aponta:

Essa é uma escolha de design deliberada. Como strings são imutáveis, é seguro compartilhá-las — múltiplas variáveis podem apontar para os mesmos bytes subjacentes sem o risco de uma modificar o que a outra vê. Também significa que copiar uma string é barato: você copia o ponteiro e o comprimento, não os bytes.

Para construir uma string modificada, converta para um tipo mutável, faça a alteração e converta de volta:

Conversões entre string, rune e byte

Os três tipos string, rune e byte são intimamente relacionados, e Go permite conversões explícitas entre eles. Cada conversão tem um significado específico.

ConversãoO que faz
string(r) onde r é um runeCria uma string com a codificação UTF-8 daquele code point
string(b) onde b é um byteCria uma string de um byte contendo aquele valor
string(n) onde n é um inteiroCria uma string com a codificação UTF-8 do code point n
[]byte(s)Copia os bytes da string em um novo []byte
[]rune(s)Decodifica a string como UTF-8 e retorna cada code point como um rune
rune(b)Amplia o valor do byte para um rune
byte(r)Trunca o valor do rune para um único byte

Não é possível converter implicitamente entre esses tipos. Tentar atribuir um valor rune ou byte diretamente a uma variável string — ou passar um onde o outro é esperado — é um compile error:

UTF-8 e Unicode

Arquivos-fonte Go são sempre UTF-8. String literals no seu código-fonte são armazenados como a codificação UTF-8 dos caracteres que você escreveu. Para texto ASCII — letras, dígitos, pontuação — cada caractere ocupa exatamente um byte, então indexar por byte e indexar por caractere é a mesma coisa. Para caracteres não-ASCII, não é.

UTF-8 é uma codificação de largura variável. Um único code point Unicode (um rune na terminologia Go) pode ocupar de 1 a 4 bytes:

  • Caracteres ASCII (U+0000 a U+007F): 1 byte
  • Caracteres como é, ñ, ü (U+0080 a U+07FF): 2 bytes
  • Caracteres CJK e a maior parte do BMP (U+0800 a U+FFFF): 3 bytes
  • Emoji e caracteres suplementares (U+10000 em diante): 4 bytes

Isso significa que len(s) retorna o número de bytes, não o número de caracteres. Para uma string com caracteres multi-byte, esses valores diferem:

Indexação retorna bytes, não runes

Como uma string é uma sequência de bytes, s[i] sempre retorna o byte na posição i, não o caractere na posição i. Para strings que contêm caracteres multi-byte, isso produz o valor bruto do byte — não o rune:

Para trabalhar com caracteres em vez de bytes, use []rune:

Iterando sobre uma string com range

O loop for range sobre uma string é consciente de UTF-8. Ele automaticamente decodifica cada code point e fornece o valor rune junto com o byte offset onde aquele rune começa:

Observe que é começa no byte offset 1 e o próximo caractere começa no byte offset 3, porque é ocupa 2 bytes. O for range lida com tudo isso automaticamente — é a forma idiomática de iterar sobre os caracteres de uma string.

O package strings

O package strings fornece o toolkit padrão para trabalhar com strings. Algumas das funções mais usadas:

FunçãoO que faz
strings.Contains(s, substr)Verifica se substr está contido em s
strings.HasPrefix(s, prefix)Verifica se s começa com prefix
strings.HasSuffix(s, suffix)Verifica se s termina com suffix
strings.Count(s, substr)Conta ocorrências não sobrepostas de substr em s
strings.Index(s, substr)Retorna o byte index da primeira ocorrência de substr
strings.Replace(s, old, new, n)Substitui as primeiras n ocorrências de old por new; -1 substitui todas
strings.ToUpper(s)Retorna s convertida para maiúsculas
strings.ToLower(s)Retorna s convertida para minúsculas
strings.TrimSpace(s)Retorna s sem espaços em branco no início e no fim
strings.Split(s, sep)Divide s em um slice de substrings separadas por sep
strings.Join(elems, sep)Une os elementos de um slice com sep entre cada um
strings.BuilderBuffer eficiente para construir strings incrementalmente

Para construir strings a partir de muitas partes, use strings.Builder em vez de concatenação — a concatenação com + cria uma nova string a cada operação, enquanto o Builder acumula bytes em um buffer e produz uma string ao final: