Go by Example

String Formatting

fmt verbs - %v, %+v, %#v, %T, width and precision, and choosing between Printf, Sprintf, and Fprintf.

The fmt package provides print functions driven by format verbs. Go's verbs are distinct from C's printf - %v prints any value in a default format, %+v adds struct field names, and %#v prints a Go syntax representation useful for debugging.

The %v verb prints values in a human-readable default format. Add + for struct field names or # for Go syntax output.

package main
 
import "fmt"
 
type Point struct {
    X, Y int
}
 
func main() {
    p := Point{3, 4}
 
    fmt.Printf("%v\n", p)   // {3 4}
    fmt.Printf("%+v\n", p)  // {X:3 Y:4}
    fmt.Printf("%#v\n", p)  // main.Point{X:3, Y:4}
    fmt.Printf("%T\n", p)   // main.Point
}

Format integers with %d (decimal), %b (binary), %o (octal), %x (hex). Control width and zero-padding with width specifiers.

package main
 
import "fmt"
 
func main() {
    n := 42
    fmt.Printf("%d\n", n)    // 42
    fmt.Printf("%b\n", n)    // 101010
    fmt.Printf("%o\n", n)    // 52
    fmt.Printf("%x\n", n)    // 2a
    fmt.Printf("%X\n", n)    // 2A
 
    // Width and padding
    fmt.Printf("%6d\n", n)   //     42 (right-aligned)
    fmt.Printf("%-6d|\n", n) // 42     (left-aligned)
    fmt.Printf("%06d\n", n)  // 000042 (zero-padded)
}

Format floats with %f (decimal), %e (scientific), %g (compact). Control precision with %.Nf.

package main
 
import "fmt"
 
func main() {
    f := 3.14159
    fmt.Printf("%f\n", f)     // 3.141590
    fmt.Printf("%.2f\n", f)   // 3.14
    fmt.Printf("%8.2f\n", f)  //     3.14
    fmt.Printf("%e\n", f)     // 3.141590e+00
    fmt.Printf("%g\n", f)     // 3.14159
}

Use Sprintf to produce a formatted string, Fprintf to write directly to an io.Writer, and Errorf to create formatted errors (with %w for wrapping).

package main
 
import (
    "fmt"
    "os"
)
 
func main() {
    // Sprintf returns a string
    msg := fmt.Sprintf("Hello, %s! You are %d years old.", "Alice", 30)
    fmt.Println(msg)
 
    // Fprintf writes to any io.Writer (here, os.Stderr)
    fmt.Fprintf(os.Stderr, "error: %s\n", "something went wrong")
 
    // Errorf creates an error; %w wraps the error for errors.Is / errors.As
    cause := fmt.Errorf("connection refused")
    err := fmt.Errorf("dial database: %w", cause)
    fmt.Println(err) // dial database: connection refused
}

In production

%+v and %#v are invaluable in debug logs and test failure messages - they surface field names and Go syntax without writing custom String() methods. Prefer fmt.Fprintf(w, ...) over fmt.Sprintf(...) + w.Write(...) to avoid an intermediate string allocation when writing to an io.Writer. Use %w in fmt.Errorf for wrappable errors; %s or %v create a new non-wrappable error that callers cannot inspect with errors.Is or errors.As.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free