Formatted Printing with fmt

The fmt package prints and formats values. Its Printf family uses verbs such as %d and %s with optional width, precision and flags, much like C's printf — plus Go-specific verbs that print any value, its type, or its Go syntax. This lesson covers the verbs you'll actually use and the functions that write to strings, files and errors.

The fmt functions

Writes to…PlainWith newlineFormatted
Standard outputPrintPrintlnPrintf
A string (returned)SprintSprintlnSprintf
Any writer (file, network, stderr)FprintFprintlnFprintf
A []byte (appended)AppendAppendlnAppendf

There's also fmt.Errorf, which formats an error message (lesson 44).

GO
package main

import (
	"fmt"
	"os"
)

func main() {
	label := fmt.Sprintf("Order #%d", 1042)
	fmt.Println(label)
	fmt.Fprintln(os.Stdout, "written to standard output")

	err := fmt.Errorf("item %q is out of stock", "lamp")
	fmt.Println(err)
}
Output
Order #1042
written to standard output
item "lamp" is out of stock

General verbs

%v prints any value in a sensible default format. With + it adds struct field names, and %#v prints the value as Go source code — useful for debugging:

GO
package main

import "fmt"

type Book struct {
	Title string
	Pages int
}

func main() {
	b := Book{"Dune", 412}
	tags := []string{"sci-fi", "classic"}
	stock := map[string]int{"dune": 3}

	fmt.Printf("%v\n", b)
	fmt.Printf("%+v\n", b)
	fmt.Printf("%#v\n", b)
	fmt.Printf("%v %#v\n", tags, tags)
	fmt.Printf("%v\n", stock)
	fmt.Printf("%T %T %T\n", b, tags, stock)
	fmt.Printf("100%%\n")
}
Output
{Dune 412}
{Title:Dune Pages:412}
main.Book{Title:"Dune", Pages:412}
[sci-fi classic] []string{"sci-fi", "classic"}
map[dune:3]
main.Book []string map[string]int
100%

%% prints a literal percent sign. Maps are always printed with their keys sorted, so the output is predictable.

Numbers

GO
package main

import "fmt"

func main() {
	n := 255
	fmt.Printf("%d %b %o %x %X %#x\n", n, n, n, n, n, n)
	fmt.Printf("%c %U\n", 9731, 9731)

	f := 1234.5678
	fmt.Printf("%f | %.2f | %.0f\n", f, f, f)
	fmt.Printf("%e | %g | %g\n", f, f, 0.000001234)
}
Output
255 11111111 377 ff FF 0xff
☃ U+2603
1234.567800 | 1234.57 | 1235
1.234568e+03 | 1234.5678 | 1.234e-06

%f defaults to six decimal places; %.2f gives two. %g uses whichever of plain or exponent notation is shorter, and is what %v uses for floats.

Strings and booleans

GO
package main

import "fmt"

func main() {
	s := "say \"hi\"\n"
	fmt.Printf("%s", s)
	fmt.Printf("%q\n", s)
	fmt.Printf("%x\n", "Go")
	fmt.Printf("%t %v\n", true, false)
}
Output
say "hi"
"say \"hi\"\n"
476f
true false

%q prints a quoted, escaped string. It's excellent for debugging, because it makes empty strings, spaces and invisible characters obvious.

Width, precision and flags

Between the % and the verb you can put flags, a minimum width and a precision. Numbers align right and strings align right by default; the - flag aligns left:

GO
package main

import "fmt"

func main() {
	items := []struct {
		name  string
		qty   int
		price float64
	}{
		{"Notebook", 4, 3.5},
		{"Pen", 10, 1.2},
		{"Backpack", 1, 39.9},
	}

	fmt.Printf("%-10s %4s %8s\n", "Item", "Qty", "Price")
	for _, it := range items {
		fmt.Printf("%-10s %4d %8.2f\n", it.name, it.qty, it.price)
	}

	fmt.Printf("[%05d] [%+d] [%x] [% x]\n", 42, 42, "hi", "hi")
	fmt.Printf("[%*d] [%-*d]\n", 6, 7, 6, 7) // width taken from an argument
	fmt.Printf("%.3s\n", "Gopher")           // precision truncates strings
}
Output
Item        Qty    Price
Notebook      4     3.50
Pen          10     1.20
Backpack      1    39.90
[00042] [+42] [6869] [68 69]
[     7] [7     ]
Gop
FlagEffect
-Left-align within the width
0Pad numbers with leading zeros
+Always show the sign; with %v, show struct field names
#Alternate format: 0x prefix for hex, Go syntax for %#v
spaceLeave a space for a missing plus sign; put spaces between bytes with % x

Argument indexes

%[n] picks the n-th argument, so you can reuse one:

GO
package main

import "fmt"

func main() {
	fmt.Printf("%[1]d in hex is %[1]x and in binary is %[1]b\n", 10)
}
Output
10 in hex is a and in binary is 1010

Mistakes fmt reports

A wrong verb or a missing argument doesn't crash; fmt prints a marker such as %!d(string=…) into the output instead. Better still, go vet finds these mistakes before you run the program:

GO
package main

import "fmt"

func main() {
	name, age := "Ana", 34
	fmt.Printf("%d is %s years old\n", name, age)
	fmt.Printf("%s has %d items\n", name)
}
go vet
main.go:7:14: fmt.Printf format %d has arg name of wrong type string
main.go:8:21: fmt.Printf format %d reads arg #2, but call has 1 arg

Try it yourself

Print a receipt with aligned columns and a total line. Try changing the widths.

Try it yourself · main.go
Edit the code, copy it into main.go in a new folder and run it with go run main.go (Go 1.27 or later). How to install Go
Output
Coffee           2.80
Croissant        3.25
Orange juice     4.10
TOTAL           10.15
Next: reading input from the keyboard.