O pacote fmt
Esta aula explora o pacote fmt em Go, cobrindo funções de saída como Print, Printf e Sprintf, os verbos de formatação, funções de entrada como Scan, e boas práticas para uso eficiente e seguro. O conteúdo inclui exemplos práticos e exercícios com respostas.
O pacote fmt é um dos pacotes mais fundamentais da biblioteca padrão de Go. Ele fornece funções para entrada e saída formatada de dados, similar ao printf e scanf de C, mas com segurança de tipo e integração com o sistema de tipos de Go. Dominar o fmt é essencial para qualquer programador Go, pois ele é usado para depuração, logging, interação com o usuário e formatação de dados para saída em arquivos ou redes.
Nesta aula, vamos cobrir as principais funções de saída (Print, Printf, Sprintf), os verbos de formatação que controlam como os valores são exibidos, as funções de entrada (Scan, Scanf, Sscan) e boas práticas para evitar erros comuns. Ao final, você será capaz de usar o pacote fmt com confiança e escrever código mais claro e eficiente.
Print, Printf, Sprintf
As funções de saída do pacote fmt são usadas para escrever dados na saída padrão (stdout) ou em uma string. As três principais são Print, Printf e Sprintf. Print simplesmente imprime seus argumentos com espaços entre eles e uma nova linha no final. Printf permite formatação usando verbos, similar à função printf de C. Sprintf funciona como Printf, mas retorna a string formatada em vez de imprimi-la.
Exemplo básico:
package main
import "fmt"
func main() {
nome := "João"
idade := 30
// Print: imprime valores separados por espaço
fmt.Print("Nome:", nome, "Idade:", idade) // Saída: Nome:João Idade:30
// Printf: formata com verbos
fmt.Printf("Nome: %s, Idade: %d\n", nome, idade)
// Sprintf: retorna string formatada
msg := fmt.Sprintf("Nome: %s, Idade: %d", nome, idade)
fmt.Println(msg)
}
Note que Print e Println adicionam espaços automaticamente entre argumentos não string, enquanto Printf exige que você especifique a formatação. Println também adiciona uma nova linha. Para saída de depuração rápida, Println é conveniente; para saída controlada, use Printf.
Verbos
Os verbos de formatação são placeholders que começam com % e controlam como um valor é exibido. Os mais comuns são:
%v: valor no formato padrão (útil para qualquer tipo)%+v: para structs, inclui nomes dos campos%#v: representação Go-syntax do valor%T: tipo do valor%d: inteiro decimal%s: string%f: ponto flutuante (float64)%t: booleano%p: ponteiro (endereço em hexadecimal)
Exemplo de uso:
package main
import "fmt"
func main() {
p := struct {
Nome string
Idade int
}{"Maria", 25}
fmt.Printf("%%v: %v\n", p) // {Maria 25}
fmt.Printf("%%+v: %+v\n", p) // {Nome:Maria Idade:25}
fmt.Printf("%%#v: %#v\n", p) // struct { Nome string; Idade int }{Nome:"Maria", Idade:25}
fmt.Printf("%%T: %T\n", p) // struct { Nome string; Idade int }
fmt.Printf("%%d: %d\n", 42) // 42
fmt.Printf("%%s: %s\n", "Olá") // Olá
fmt.Printf("%%f: %f\n", 3.14) // 3.140000
fmt.Printf("%%t: %t\n", true) // true
x := 10
fmt.Printf("%%p: %p\n", &x) // 0xc0000b2000 (exemplo)
}
Você também pode usar flags como %+d para exibir sinal, % d para espaço em branco, %0d para preenchimento com zeros, e especificar largura e precisão, como %5.2f (largura 5, 2 casas decimais).
Scan
As funções de entrada Scan, Scanf e Sscan (e suas variantes Scanln, Sscanln) leem dados da entrada padrão (stdin) ou de uma string. Elas são úteis para interação com o usuário ou para parsing de texto simples.
Exemplo de Scanf:
package main
import "fmt"
func main() {
var nome string
var idade int
fmt.Print("Digite seu nome e idade: ")
n, err := fmt.Scanf("%s %d", &nome, &idade)
if err != nil {
fmt.Println("Erro:", err)
return
}
fmt.Printf("Lidos %d itens: nome=%s, idade=%d\n", n, nome, idade)
}
Scanf usa verbos de formatação para interpretar a entrada. É importante verificar o erro retornado, pois a entrada pode não corresponder ao formato esperado. Scan lê tokens separados por espaços, enquanto Scanln lê até uma nova linha. Sscan e Sscanln leem de uma string.
Exemplo com Sscan:
package main
import "fmt"
func main() {
entrada := "42 3.14 verdadeiro"
var i int
var f float64
var b bool
n, err := fmt.Sscan(entrada, &i, &f, &b)
if err != nil {
fmt.Println("Erro:", err)
return
}
fmt.Printf("Lidos %d valores: %d, %f, %t\n", n, i, f, b)
}
Lembre-se de que as funções Scan esperam que os argumentos sejam ponteiros para as variáveis onde os valores serão armazenados.
Boas práticas
Ao usar o pacote fmt, siga estas boas práticas para evitar erros comuns:
- Sempre verifique erros nas funções de entrada (
Scan,Scanf, etc.). Ignorar erros pode levar a valores não inicializados. - Use verbos específicos em vez de
%vquando possível, para maior clareza e controle. - Evite
Printfsem necessidade de formatação; prefiraPrintlnpara saída simples. - Cuidado com injeção de formato: nunca passe strings do usuário diretamente como formato em
Printf. Use%souPrintpara evitar vulnerabilidades. - Prefira
fmt.Fprintfpara escrever em arquivos ou buffers, pois permite maior controle. - Use
fmt.Sprintfpara construir strings complexas em vez de concatenação.
Exemplo de injeção de formato (perigoso):
// Perigoso: se o usuário digitar "%s%s%s%s", pode causar pânico
fmt.Printf(entradaUsuario)
// Correto: tratar a entrada como valor
fmt.Printf("%s", entradaUsuario)
Referências
- Documentação oficial do pacote fmt
- Strings, bytes, runes e caracteres em Go
- Go by Example: String Formatting
- Cheat sheet de formatação fmt
- How To Use the fmt Package in Go (DigitalOcean)
Exercícios
-
Escreva um programa que leia o nome e a idade do usuário usando
fmt.Scanfe imprima uma mensagem como "Olá, [nome]! Você tem [idade] anos." usandofmt.Printf.✓ Resposta:package main import "fmt" func main() { var nome string var idade int fmt.Print("Digite seu nome e idade: ") _, err := fmt.Scanf("%s %d", &nome, &idade) if err != nil { fmt.Println("Erro na leitura:", err) return } fmt.Printf("Olá, %s! Você tem %d anos.\n", nome, idade) } -
Use
fmt.Sprintfpara criar uma string formatada que exiba um número inteiro com largura 5 e preenchimento com zeros à esquerda. Imprima a string resultante.✓ Resposta:package main import "fmt" func main() { num := 42 formatado := fmt.Sprintf("%05d", num) fmt.Println(formatado) // "00042" } -
Escreva um programa que leia uma linha de texto do usuário usando
bufio.Scannere depois usefmt.Sscanfpara extrair um número inteiro e uma string separados por espaço.✓ Resposta:package main import ( "bufio" "fmt" "os" ) func main() { scanner := bufio.NewScanner(os.Stdin) fmt.Print("Digite um número e uma palavra: ") scanner.Scan() linha := scanner.Text() var num int var palavra string _, err := fmt.Sscanf(linha, "%d %s", &num, &palavra) if err != nil { fmt.Println("Erro ao parsear:", err) return } fmt.Printf("Número: %d, Palavra: %s\n", num, palavra) } -
Crie um programa que use
fmt.Fprintfpara escrever uma string formatada em umbytes.Buffere depois imprima o conteúdo do buffer.✓ Resposta:package main import ( "bytes" "fmt" ) func main() { var buf bytes.Buffer nome := "Ana" idade := 28 fmt.Fprintf(&buf, "Nome: %s, Idade: %d", nome, idade) fmt.Println(buf.String()) // "Nome: Ana, Idade: 28" } -
Explique por que o código abaixo é perigoso e como corrigi-lo:
fmt.Printf(entradaUsuario).✓ Resposta:O código
fmt.Printf(entradaUsuario)é perigoso porque permite injeção de formato. Se o usuário digitar algo como%s%s%s%s, a função tentará interpretar a string como formato, podendo causar pânico ou comportamento inesperado. A correção é usarfmt.Printf("%s", entradaUsuario)oufmt.Print(entradaUsuario)para tratar a entrada como um valor a ser impresso, não como formato.