O problema eterno: "0" à esquerda ou "deslocamento à direita"?
\n\nVocê já se perguntou por que em consultas SQL os números de pedido aparecem como 00042, e em logs como 23:05:07? É simples: números preenchidos com zeros são alinhados por largura e não quebram a ordenação. Mas em Go, ao contrário de Python ou JavaScript, não existe um método nativo zfill. E é aí que a coisa fica interessante.
No Stack Overflow, a pergunta "How to pad a number with zeros when printing?" acumulou milhares de visualizações e dezenas de respostas. As pessoas buscam não apenas uma solução, mas uma solução rápida. Neste artigo, vamos analisar todas as formas de preencher um número com zeros em Go, comparar seu desempenho e escolher a melhor para o seu projeto.
\n\nPor que isso é importante para um desenvolvedor Go?
\n\nGo é uma linguagem para programação de sistemas, onde cada nanossegundo conta. Se você está escrevendo um serviço de alta carga, a formatação incorreta de strings pode se tornar um gargalo. Especialmente se você estiver formatando milhares de números por segundo.
\n\n- \n
- Registro de logs: carimbos de data/hora, IDs de requisição, códigos de erro. \n
- Geração de relatórios: CSV, JSON, tabelas. \n
- Protocolos de rede: comprimento fixo de campos. \n
- Jogos e gráficos: alinhamento de texto no console. \n
Ignorar o desempenho pode fazer com que seu serviço gaste 10% do tempo apenas formatando números. Não acredita? Vamos verificar.
\n\nMétodo nº1: O clássico via fmt.Sprintf
\n\nA maneira mais óbvia e frequentemente recomendada é usar fmt.Sprintf com o especificador de formato %0Xd, onde X é a largura total do número incluindo os zeros.
package main\n\nimport "fmt"\n\nfunc main() {\n num := 42\n padded := fmt.Sprintf("%05d", num)\n fmt.Println(padded) // Saída: 00042\n}\n\n\nAqui, %05d significa: "exiba o número como um inteiro decimal, preencha à esquerda com zeros até uma largura de 5 caracteres". Se o número já for mais longo (por exemplo, 123456), os zeros não são adicionados — a largura é ignorada.
Vantagens:
\n- \n
- Simples e legível. \n
- Suporta números negativos (o sinal ficará à esquerda dos zeros). \n
- Funciona com diferentes tipos:
%05dpara int,%05.2fpara float. \n
Desvantagens:
\n- \n
- Usa reflexão internamente no
fmt— é lento. \n - Cria uma string extra e interface{}. \n
- Não é adequado para caminhos críticos de desempenho. \n
Método nº2: strconv.FormatInt rápido com preenchimento manual
\n\nSe o desempenho for crítico, use o pacote strconv. Ele trabalha diretamente com bytes, sem reflexão.
package main\n\nimport (\n "fmt"\n "strconv"\n "strings"\n)\n\nfunc padNumber(num int64, width int) string {\n s := strconv.FormatInt(num, 10)\n if len(s) >= width {\n return s\n }\n return strings.Repeat("0", width-len(s)) + s\n}\n\nfunc main() {\n fmt.Println(padNumber(42, 5)) // 00042\n}\n\n\nVantagens:
\n- \n
- 2 a 3 vezes mais rápido que
fmt.Sprintf. \n - Sem reflexão. \n
- Controle total sobre a lógica. \n
Desvantagens:
\n- \n
- Mais código. \n
- É necessário tratar números negativos separadamente (sinal à esquerda dos zeros). \n
strings.Repeatcria uma nova string — pode ser otimizado ainda mais. \n
Método nº3: Desempenho máximo com strings.Builder
\n\nPara quem quer extrair o máximo de desempenho, existe uma maneira sem alocações extras — usar strings.Builder e alocar o buffer antecipadamente.
package main\n\nimport (\n "fmt"\n "strconv"\n "strings"\n)\n\nfunc padNumberFast(num int64, width int) string {\n s := strconv.FormatInt(num, 10)\n if len(s)