Pular para o conteúdo principal
O pacote fmt em profundidade

O pacote fmt em profundidade

11 min de leitura

Arquivado emLinguagem de Programação Goem

Domine o pacote fmt do Go — verbos de formatação, largura e alinhamento, as interfaces Stringer e GoStringer, e a família completa de funções de impressão e leitura.

Todo programa Go usa fmt. É o pacote que você utiliza para imprimir um valor no terminal, formatar uma string com dados dinâmicos, registrar uma mensagem na saída de erro, ou construir uma representação legível de um tipo. Apesar da familiaridade, a maioria dos desenvolvedores usa apenas uma fração do que o fmt oferece. Entender o conjunto completo de verbos de formatação, como largura e alinhamento funcionam, e como o fmt interage com seus próprios tipos tornará sua saída mais legível e seu código mais expressivo.

As famílias de funções

O fmt expõe três famílias de funções de saída, distinguidas pelo destino do resultado, mais uma quarta para leitura de entrada.

A família Print escreve para a saída padrão (os.Stdout):

A família Fprint escreve para qualquer io.Writer. Esta é a forma mais flexível — funciona com arquivos, conexões de rede, HTTP response writers, ou qualquer tipo que satisfaça io.Writer:

A família Sprint retorna uma string formatada em vez de escrever em qualquer lugar. Use essas funções quando precisar construir uma string para usar depois:

A família Scan lê e interpreta valores da entrada padrão ou de uma string, abordada no final deste artigo.

FamíliaDestinoRetorno
Print/Println/Printfos.Stdout(n int, err error)
Fprint/Fprintln/Fprintfqualquer io.Writer(n int, err error)
Sprint/Sprintln/Sprintfstring
Scan/Scanln/Scanfos.Stdin(n int, err error)

Espaços entre argumentos

Print e Sprint adicionam um espaço entre dois operandos quando nenhum deles é uma string. Println e Sprintln sempre adicionam espaços entre todos os operandos. Printf e Sprintf nunca adicionam espaços — a string de formato controla tudo.

Verbos de formatação

Um verbo de formatação é um % seguido de um ou mais caracteres. Ele diz ao fmt como representar um valor. Os verbos se aplicam ao próximo argumento na chamada.

Verbos de uso geral

Esses funcionam com qualquer tipo e são os mais usados:

VerboDescrição
%vFormato padrão para o valor
%+vStruct com nomes dos campos
%#vRepresentação em sintaxe Go
%TTipo do valor

%v é o coringa. Ele produz a representação mais natural e legível de cada tipo — decimal para inteiros, notação de ponto flutuante para floats, true/false para booleans, e para structs, os valores dos campos entre chaves:

%+v adiciona nomes de campos à saída de struct — útil para debug quando a ordem dos campos é ambígua. %#v vai além e produz sintaxe de literal Go válida, com o nome do tipo qualificado pelo package. %T retorna o próprio tipo, o que é útil quando você precisa registrar que tipo de valor chegou a uma função.

Verbos para inteiros

VerboRepresentação
%dDecimal
%bBinário
%oOctal
%OOctal com prefixo 0o
%xHexadecimal, minúsculas
%XHexadecimal, maiúsculas
%cCaractere (code point Unicode)
%UFormato Unicode: U+0041

Hexadecimal é comumente usado ao inspecionar bytes brutos, codificar valores, ou gerar IDs. O verbo %c é útil ao trabalhar com runes: em vez de imprimir o valor numérico, ele imprime o caractere que representa.

Verbos para strings e bytes

VerboRepresentação
%sString bruta ou slice de bytes
%qCom aspas duplas e sequências de escape Go
%xCodificação hex de cada byte, minúsculas
%XCodificação hex de cada byte, maiúsculas

%q é especialmente útil para debug: ele envolve a string em aspas e escapa caracteres não imprimíveis, para que você veja exatamente quais bytes estão na string — incluindo tabs, newlines e bytes nulos que seriam invisíveis de outra forma. %x em uma string a trata como sequência de bytes e codifica cada byte em hexadecimal.

Verbos para ponto flutuante

VerboRepresentação
%fNotação decimal, sem expoente
%eNotação científica, minúsculas (1.23e+04)
%ENotação científica, maiúsculas (1.23E+04)
%g%e para expoentes grandes, %f nos demais casos
%G%E para expoentes grandes, %f nos demais casos

%g é um padrão inteligente: ele escolhe %e quando o expoente seria muito grande ou muito pequeno, e %f nos demais casos. A precisão padrão para %f é seis casas decimais; você pode controlar isso com um especificador de precisão, mostrado na próxima seção.

Outros verbos

VerboTipoDescrição
%tbooltrue ou false
%ppointerEndereço de memória em hexadecimal, com prefixo 0x

%p é útil para debug de aliasing de pointer — quando você quer verificar se dois pointers apontam para o mesmo endereço de memória.

Largura, precisão e alinhamento

Entre o % e o verbo, você pode inserir uma largura, uma precisão e flags que controlam como o valor é renderizado.

O formato geral é:

%[flags][largura][.precisão]verbo

Largura define o número mínimo de caracteres na saída. Se o valor for menor, ele é preenchido com espaços à esquerda (alinhado à direita por padrão):

Precisão tem significados diferentes para tipos diferentes:

  • Para floats: número de dígitos decimais após o ponto
  • Para strings: número máximo de caracteres a imprimir

Você também pode fornecer largura e precisão dinamicamente usando *, que lê o próximo argumento:

Isso é útil quando a largura da coluna desejada é determinada em tempo de execução — por exemplo, ao alinhar saída em uma tabela cujas larguras de coluna dependem dos dados.

FlagEfeito
-Alinhar à esquerda (padrão é à direita)
0Preencher com zeros em vez de espaços
+Sempre imprimir sinal para números
(espaço)Adicionar espaço à esquerda para números positivos
#Forma alternativa (0x para hex, 0 para octal, etc.)

A interface Stringer

Quando você passa um valor para uma função fmt com %v ou %s, o pacote verifica se o valor implementa a interface fmt.Stringer:

Se implementar, fmt chama String() e usa o resultado. É assim que você define uma representação legível para seus próprios tipos:

Sem String(), imprimir um Direction com %v mostraria o inteiro bruto — 0, 1, 2, 3. Com String() implementado:

Note que %d ainda imprime o inteiro subjacente. Formatação específica por verbo ignora Stringer. Apenas %v, %s e a formatação padrão (via Println) invocam String().

Evite chamar fmt dentro de String()

Se seu método String() chamar fmt.Sprintf com %v no próprio receiver, você causará recursão infinita — fmt chama String(), que chama fmt, e assim por diante até o stack estourar. Converta o receiver para seu tipo subjacente primeiro para quebrar o ciclo: fmt.Sprintf("Direction(%d)", int(d)).

Implementar Stringer é uma das coisas mais impactantes que você pode fazer pela usabilidade de um tipo. Torna os valores legíveis em saídas de log, mensagens de erro e prints de debug sem exigir que os chamadores conheçam os internos do tipo.

A interface GoStringer

Uma interface paralela controla como um valor aparece ao ser formatado com %#v:

%#v é projetado para produzir saída que pareça código Go válido. Para tipos onde a saída padrão de %#v é verbosa ou incompleta, implementar GoString dá a você controle:

Sem GoString, o %#v padrão produziria main.Color{R:0xff, G:0x80, B:0x00} — próximo, mas com valores decimais. Implementar GoString permite usar notação hexadecimal, adicionar um comentário, ou produzir qualquer string que melhor transmita o significado do valor como código fonte.

GoStringer é menos necessário que Stringer, mas é valioso para tipos que aparecem frequentemente em saída de testes ou sessões de debug onde ver uma representação em literal Go economiza tempo.

fmt.Errorf e formatação de erros

fmt.Errorf cria um error cuja mensagem é uma string formatada:

O verbo %w envolve outro error no novo, preservando-o em uma cadeia que errors.Is e errors.As podem percorrer:

Usar %v inclui a mensagem do error original como texto simples, mas quebra a cadeia — o error original se torna inacessível para os chamadores. Use %w quando os chamadores possam precisar inspecionar o error encapsulado; use %v quando o error original é um detalhe de implementação que não deve ser inspecionado. O artigo sobre errors cobre encapsulamento de errors e travessia de cadeia em detalhes.

Lendo entrada com Scan

A família Scan lê valores separados por espaço da entrada padrão e os interpreta como variáveis:

fmt.Scan lê tantos tokens delimitados por espaço ou newline quantos forem os argumentos. fmt.Scanln para em uma newline — não atravessa quebras de linha para encontrar argumentos adicionais. fmt.Scanf interpreta de acordo com uma string de formato, de forma similar ao Printf:

Para fazer scan de uma string em vez de stdin, use fmt.Sscan, fmt.Sscanln, ou fmt.Sscanf:

Para entrada linha a linha, prefira bufio.Scanner

fmt.Scan é conveniente para programas pequenos e exercícios, mas trata erros de forma precária e se comporta de maneira imprevisível com entrada mista. Para qualquer código em produção que leia stdin linha a linha, o artigo sobre os e bufio cobre bufio.Scanner, que é mais robusto e explícito.

O pacote fmt não é apenas uma coleção de funções de impressão — é um motor de formatação construído em torno de interfaces. Uma vez que você entende o sistema de verbos, as flags de largura e precisão, e como Stringer conecta seus tipos a esse motor, você tem tudo o que precisa para produzir saída clara e estruturada em qualquer contexto.