AlgoMaster Logo

Input & Output (fmt package)

Last Updated: May 22, 2026

Medium Priority
7 min read

The fmt package handles almost all the printing and reading you'll do in early Go programs. You've already seen fmt.Println in earlier work; this lesson covers the full family of print and scan functions, the format verbs that control what comes out, and the rules for reading user input safely.

Print, Println, Printf

Three functions cover most of your printing needs, and the differences between them are worth pinning down once. They all write to standard output, but they treat spaces, newlines, and formatting differently.

Look at the first two lines. fmt.Print doesn't add spaces between its string arguments and doesn't end with a newline. That's why OrderConfirmed runs together. You're responsible for any whitespace and line breaks yourself.

fmt.Println is the opposite. It inserts a single space between arguments and tacks on a newline at the end. It's what you use when you just want to see a value while debugging. There's one wrinkle though: Println adds spaces between any two arguments, even if they're both strings. So fmt.Println("Total:", 49.99) prints Total: 49.99, not Total:49.99.

fmt.Printf gives you full control. The first argument is a format string with verbs (the % codes) that the remaining arguments fill in. It does not add a trailing newline, so you almost always end the format string with \n.

A more realistic example showing when each one is useful:

Use Print when you're building output piece by piece on the same line (progress dots, partial prompts). Use Println when you just want a value printed with default formatting and a newline. Use Printf when the exact layout matters: currency, padding, columns, anything where precision counts.

Sprintf: Build a String Instead of Printing It

Sometimes you don't want to print anything yet. You want to format a value into a string and use it later, maybe to store it in a variable, return it from a function, or pass it to another function. That's what fmt.Sprintf does. The leading S stands for "string".

Sprintf takes the same format string and verbs as Printf, but instead of writing to the screen, it returns a string. This is how you build labels, log messages, error messages, file names, and any other text that needs to be shaped from multiple values.

There's also fmt.Errorf, which works just like Sprintf but returns an error value instead of a string. You'll see it like this:

For now, just know Errorf exists and uses the same format verbs.

Format Verbs

The % symbols in a format string are called verbs. Each verb tells Printf how to interpret the next argument. Picking the right verb is the difference between 49.989999999999995 and $49.99.

Here's a reference table of the verbs you'll use most:

VerbMeaningExample ArgumentExample Output
%vDefault format for any valueProduct{Name: "Pen"}{Pen}
%+vStruct with field namesProduct{Name: "Pen"}{Name:Pen}
%#vGo syntax representation[]int{1, 2}[]int{1, 2}
%TType of the value49.99float64
%dInteger in base 104242
%bInteger in binary5101
%oInteger in octal810
%xInteger in lowercase hex255ff
%XInteger in uppercase hex255FF
%fFloating point, default precision3.143.140000
%.2fFloat with 2 decimal places3.141593.14
%sString"Pen"Pen
%qQuoted string (with escapes)"Pen""Pen"
%tBooleantruetrue
%pPointer address&x0xc0000140a0
%%A literal % character(none)%

A quick tour of the three %v variants, because they confuse people:

%v prints just the values. %+v adds field names, which is what you want when debugging a struct with several fields. %#v prints the value in valid Go syntax, useful when you want to paste it back into source code. %T skips the value entirely and tells you the type, which is handy when a variable is doing something unexpected.

The %d family covers integers in different number systems. %d is decimal (the everyday case). %b, %o, %x, and %X give you binary, octal, lowercase hex, and uppercase hex. You'll mostly use %d; the others come up in low-level work or when printing things like color codes.

For floats, the most useful one is %.2f, which forces two decimal places. That's what you'll use for prices.

Note that %.0f rounds. 19.5 becomes 20, not 19. Go uses banker's rounding (round half to even), so 2.5 also rounds to 2 and 3.5 rounds to 4. This rarely matters, but it's worth knowing if you're surprised by a result.

%q is the quoted-string verb. It wraps a string in double quotes and escapes any special characters inside it. This is useful when you want to log a string and you need to see exactly what's in it, including any hidden whitespace.

The third line is the giveaway. With %s you'd see an actual tab. With %q you see \t, so you can tell the string has a tab in it.

Width and Precision

Verbs accept an optional width (minimum number of characters) and precision (decimal places for floats, max characters for strings). This is how you build aligned columns and zero-padded numbers.

The pattern is %[flags][width][.precision]verb. A few examples make it concrete:

Walking through these:

  • %5d reserves 5 characters and right-aligns the number (default).
  • %-5d reserves 5 characters but left-aligns (the - flag flips the alignment).
  • %05d reserves 5 characters and pads with zeros on the left instead of spaces.
  • %10s right-aligns the string in a 10-character field.
  • %-10s left-aligns the string in a 10-character field. This is the one you'll use for product tables.
  • %8.2f reserves 8 characters total and forces 2 decimal places. The number 3.14 takes 4 characters, leaving 4 spaces of left padding.

Aligned columns are where these flags pay off. Here's a product table that uses them:

Each column lines up because every row uses the same widths. The product name is left-aligned in 20 characters (%-20s), the price is right-aligned in 7 characters with 2 decimals (%7.2f), and the stock count is right-aligned in 6 characters (%6d).

Reading Input with Scan, Scanln, and Scanf

The fmt package can read user input too. The three main functions are Scan, Scanln, and Scanf. They mirror the print family, but in reverse: they read values from standard input and store them through pointers.

The pointer part is important. To read into a variable, you pass its address (&name), not the variable itself. That's because the function needs to modify the variable, and Go passes everything by value. For now, just remember the &.

Here's fmt.Scan, which reads space-separated values:

Sample run:

fmt.Scan reads tokens separated by whitespace (spaces, tabs, or newlines). It tries to convert each token to match the type of the corresponding pointer. If the user types Priya hello and we expected an int for the second value, the conversion fails and err is non-nil. Always check the error. Ignoring it means your program will happily run with whatever the variable's zero value was.

fmt.Scanln is similar, but it stops reading at the newline. If the user types fewer values than expected before pressing Enter, it returns an error. If they type more, the extras stay in the input buffer for the next read.

Sample run:

fmt.Scanf uses a format string, like Printf in reverse. It matches the input against the format, picking out values where the verbs are:

Sample run:

There's a real limitation here: these functions treat whitespace as a separator, which means you can't easily read a multi-word string with Scan. Asking for a "full name" with fmt.Scan(&name) will only capture the first word. To read a whole line, including spaces, you need bufio.Scanner.

Writing to Other Destinations

So far, every print has gone to standard output (your terminal). The fmt package has a parallel set of functions that write to any destination instead, prefixed with F for "file" (though they work with more than just files). The two you'll see most are fmt.Fprintln and fmt.Fprintf.

The most common use is writing errors to standard error instead of standard output. This matters in real programs: standard output is for the program's actual results, and standard error is for diagnostic messages. When you pipe output to another program or a log file, you usually want errors to stay visible.

Output (on stdout):

Output (on stderr):

os.Stdout and os.Stderr are both io.Writer values. Any type that satisfies io.Writer can be passed to the Fprint family, including files, network connections, and in-memory buffers. The takeaway for now: when you see fmt.Fprintf(w, ...) in someone else's code, w is just a destination, and fmt works with any of them.

Putting It Together: Order Receipt

The following program exercises most of what's been covered: reading user input, formatting numbers and strings, aligning columns, and printing a polished receipt.

Sample run:

Notice the float64(quantity) conversion. Go won't multiply an int by a float64 directly. Also notice every Scan checks its error. Skip those checks and a typo in the input silently leaves a variable at its zero value, which produces wrong results without any warning.