Printing & Formatting with fmt

Go · Lesson 9 of 32 6 min read Updated

Almost every Go program prints something: a result, a report, a log line or an error. The standard fmt package does all of it. Its Printf family uses small codes called verbs, such as %d and %s, to control exactly how each value appears.

In this lesson you will learn the verbs you need every day, how to line up columns, how Go prints structs, slices and maps, and how to read simple input from the keyboard.

The printing functions

FunctionOutput goes toNewline added?
fmt.Print(a, b)Screen (standard output)No
fmt.Println(a, b)ScreenYes, and spaces between values
fmt.Printf(format, a, b)ScreenNo, add \n yourself
fmt.Sprintf(format, a, b)Returned as a stringNo
fmt.Fprintf(w, format, a)Any writer, such as a file or os.StderrNo

The S versions build a string instead of printing, and the F versions write to a destination you choose. All of them understand the same verbs.

The everyday verbs

A verb starts with % and is replaced by the next argument:

Go
package main

import "fmt"

func main() {
	name := "Priya"
	age := 20
	cgpa := 8.756

	fmt.Printf("%s is %d years old\n", name, age)
	fmt.Printf("CGPA: %.2f\n", cgpa)
	fmt.Printf("CGPA: %v\n", cgpa)
	fmt.Printf("%q has type %T\n", name, name)
	fmt.Printf("%t %c %U\n", age > 18, 'G', 'G')
	fmt.Printf("%d%% complete\n", 75)
}
Output
Priya is 20 years old
CGPA: 8.76
CGPA: 8.756
"Priya" has type string
true G U+0047
75% complete
VerbMeaning
%vThe value in its default format (works for anything)
%dInteger in decimal
%f, %.2fFloat, with 2 digits after the point (rounded)
%sString
%qQuoted string or character, with Go escapes
%TThe type of the value
%ttrue or false
%c, %UThe character for a rune, and its Unicode number
%%A literal percent sign

When in doubt, use %v. For floats, %v prints the shortest form that represents the value exactly, so 8.756 stays 8.756.

Width, padding and number bases

A number between % and the verb sets a minimum width. A - aligns left, and a 0 pads numbers with zeros. You can also print numbers in binary, octal and hexadecimal:

Go
package main

import "fmt"

func main() {
	n := 255
	fmt.Printf("%d %b %o %x %X\n", n, n, n, n, n)
	fmt.Printf("[%5d] [%-5d] [%05d]\n", 42, 42, 42)
	fmt.Printf("[%8.2f] [%-8.2f] [%e]\n", 3.14159, 3.14159, 123456.789)
}
Output
255 11111111 377 ff FF
[   42] [42   ] [00042]
[    3.14] [3.14    ] [1.234568e+05]

Widths make neat tables. Here is a bill from a stationery shop, with the item name left-aligned in 14 characters and the price right-aligned in 8:

Go
package main

import "fmt"

func main() {
	items := []string{"Notebook", "Gel pen", "Geometry box"}
	prices := []float64{45, 10, 120.5}

	total := 0.0
	for i, item := range items {
		fmt.Printf("%-14s ₹%8.2f\n", item, prices[i])
		total += prices[i]
	}
	fmt.Printf("%-14s ₹%8.2f\n", "Total", total)
}
Output
Notebook       ₹   45.00
Gel pen        ₹   10.00
Geometry box   ₹  120.50
Total          ₹  175.50

For strings, Go counts the width in characters (runes), not bytes, so Hindi or Tamil names line up correctly too, as long as the font gives every character the same width.

Printing structs, slices and maps

%v knows how to print every type. Two variants add detail: %+v adds struct field names, and %#v prints the value as Go source code, which is great for debugging.

Go
package main

import "fmt"

type Student struct {
	Name  string
	Marks int
}

func main() {
	s := Student{"Kabir", 88}
	fmt.Printf("%v\n", s)
	fmt.Printf("%+v\n", s)
	fmt.Printf("%#v\n", s)
	fmt.Println(&s)

	cities := []string{"Pune", "Kochi"}
	fmt.Printf("%v %q\n", cities, cities)

	stock := map[string]int{"pens": 40, "erasers": 15}
	fmt.Println(stock)

	var empty []int
	var err error
	fmt.Println(empty, len(empty), err)
}
Output
{Kabir 88}
{Name:Kabir Marks:88}
main.Student{Name:"Kabir", Marks:88}
&{Kabir 88}
[Pune Kochi] ["Pune" "Kochi"]
map[erasers:15 pens:40]
[] 0 <nil>

A pointer to a struct prints with a leading &. When printing a map, fmt sorts the keys, so the output is always the same. (Looping over a map with for range is not sorted; see Maps.) A nil error or pointer prints as <nil>. You will learn how a type can choose its own %v text with a String() method in Interfaces.

Building strings and spotting mistakes

Sprintf is the easy way to build a formatted string, such as an invoice number:

Go
package main

import (
	"fmt"
	"os"
)

func main() {
	invoice := fmt.Sprintf("INV-%04d", 37)
	fmt.Println(invoice)

	fmt.Fprintln(os.Stderr, "warning: printer offline")

	fmt.Printf("%d items\n", "five")
	fmt.Printf("%s and %s\n", "chai")
}
Output
INV-0037
warning: printer offline
%!d(string=five) items
chai and %!s(MISSING)

Fprintln(os.Stderr, ...) writes to standard error, the stream for warnings and errors, which the terminal shows alongside normal output. The last two lines show how fmt reports bad verbs instead of crashing: %!d(string=five) means "the %d verb got a string", and %!s(MISSING) means an argument is missing. go vet finds these mistakes before you run the program.

Reading input with fmt.Scan

fmt.Scan reads space-separated values from the keyboard into variables. You pass the address of each variable with & (explained in Pointers) so Scan can fill it:

Go
package main

import "fmt"

func main() {
	var name string
	var age int

	fmt.Print("Enter name and age: ")
	if _, err := fmt.Scan(&name, &age); err != nil {
		fmt.Println("Could not read input:", err)
		return
	}
	fmt.Printf("Hello %s, next year you will be %d\n", name, age+1)
}
Output
Enter name and age: Farhan 23
Hello Farhan, next year you will be 24

Scan returns how many values it read and an error, for example when the user types letters where a number is expected. For whole lines that may contain spaces, use bufio.Scanner, covered in Standard Library: os, io & time.

Common mistakes

  • Forgetting \n in Printf. The next output continues on the same line.
  • Using %d for a float or %f for an int. You get %!d(float64=...) text. Use %v if unsure.
  • Passing the variable instead of its address to Scan. fmt.Scan(name) cannot fill name; write fmt.Scan(&name).
  • Printing a map with for range and expecting a fixed order. Only fmt's whole-map printing is sorted.

Key points

  • Println is quick; Printf gives full control with verbs; Sprintf returns a string.
  • %v prints any value; %+v adds field names; %#v shows Go syntax; %T shows the type.
  • Width and precision such as %-14s and %8.2f line up tables.
  • fmt prints maps with sorted keys and nil values as <nil>.
  • Wrong verbs appear as %!verb(...) in the output; go vet catches them early.

Exercise

Print a marksheet for Ananya with three subjects: Maths 91, Physics 78 and Chemistry 84. Each line should show the subject left-aligned in 12 characters and the marks right-aligned in 4. Finish with the percentage to one decimal place, followed by a % sign, and create a roll number string ROLL-0042 with Sprintf.

Show solution
Go
package main

import "fmt"

func main() {
	subjects := []string{"Maths", "Physics", "Chemistry"}
	marks := []int{91, 78, 84}

	roll := fmt.Sprintf("ROLL-%04d", 42)
	fmt.Println("Ananya", roll)

	total := 0
	for i, sub := range subjects {
		fmt.Printf("%-12s%4d\n", sub, marks[i])
		total += marks[i]
	}
	percent := float64(total) / 3
	fmt.Printf("Percentage: %.1f%%\n", percent)
}
Output
Ananya ROLL-0042
Maths         91
Physics       78
Chemistry     84
Percentage: 84.3%

The total is 253 and 253 divided by 3 is 84.33..., which %.1f rounds to 84.3. %% prints the percent sign.

Quick Quiz

1. Which verb adds field names when printing a struct?

2. What does fmt.Sprintf("%05d", 42) return?

3. In what order does fmt.Println print the keys of a map?

4. What does %T print for the value 3.5?

Learn Go with a mentor

Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call