The fmt
package contains printf
. It's very similar to the C equivalent.
These three need a format string:
fmt.Sprintf
returns a string.fmt.Fprintf
takes any objects that implementsio.Writer
for exampleos.Stdout
andos.Stderr
.fmt.Printf
prints to stdout.
The following are similar but do not need a format string:
fmt.Print
andfmt.Println
fmt.Fprint
fmt.Sprint
To format strings we can use verbs (also known as switches). For more information on switches, see the fmt package source.
%d
: digits = numbers.%nd
: n = width of number. Right justified and padded with spaces. To left justify use-
like%-nd
. Ifn
is less than the number of digits nothing happens.%b
: number in binary.%c
:chr(int)
, prints the character corresponding to the number.%x
: hex.
%f
: float.%n.mf
: n = decimal width, m = float width. Right justified. To left justify use-
like%-n.mf
. If n is less than the number of digits nothing happens.%e
and%E
: scientific notation (output is a bit different from each other).
%v
or value: catch all format. Will print based on value.%+v
: will print struct's field names if we are printing a struct. Has no effect on anything else.%#v
: prints code that will generate that output. For example for a struct instance it will give code that creates such a struct instance and initializes it with the current values of the struct instance.
%q
: double-quotes the strings before printing and also prints any invisible characters.%s
: string.%ns
: control width of string. Right justified, padded with spaces. To left justify use-
like%-ns
. If n is less than the length of the string, nothing happens.
%t
: boolean.%T
: prints the type of a value. For exampleint
ormain.myType
.
// 02.5-01-print-verbs.gos
package main
import "fmt"
type myType struct {
field1 int
field2 string
field3 float64
}
func main() {
// int
fmt.Println("int:")
int1 := 123
fmt.Printf("%v\n", int1) // 123
fmt.Printf("%d\n", int1) // 123
fmt.Printf("|%6d|\n", int1) // | 123|
fmt.Printf("|%-6d|\n", int1) // |123 |
fmt.Printf("%T\n", int1) // int
fmt.Printf("%x\n", int1) // 7b
fmt.Printf("%b\n", int1) // 1111011
fmt.Printf("%e\n", int1) // %!e(int=123)
fmt.Printf("%c\n", int1) // { - 0x7B = 123
fmt.Println()
// float
fmt.Println("float:")
float1 := 1234.56
fmt.Printf("%f\n", float1) // 1234.560000
fmt.Printf("|%3.2f|\n", float1) // |1234.56|
fmt.Printf("|%-3.2f|\n", float1) // |1234.56|
fmt.Printf("%e\n", float1) // 1.234560e+03
fmt.Printf("%E\n", float1) // 1.234560E+03
fmt.Println()
// string
fmt.Println("string:")
string1 := "Petra"
fmt.Printf("%s\n", string1) // Petra
fmt.Printf("|%10s|\n", string1) // | Petra|
fmt.Printf("|%-10s|\n", string1) // |Petra |
fmt.Printf("%T\n", string1) // string
fmt.Println()
// boolean
fmt.Println("boolean:")
boolean1 := true
fmt.Printf("%t\n", boolean1) // true
fmt.Printf("%T\n", boolean1) // bool
fmt.Println()
// struct type
fmt.Println("struct:")
struct1 := myType{10, "Ender", -10.2}
fmt.Printf("%v\n", struct1) // {10 Ender -10.2}
fmt.Printf("%+v\n", struct1) // {field1:10 field2:Ender field3:-10.2}
fmt.Printf("%#v\n", struct1) // main.myType{field1:10, field2:"Ender", field3:-10.2}
fmt.Printf("%T\n", struct1) // main.myType
}
As expected Go has Scan
functions for reading input. Like Printf
the package description is comprehensive.
The functions read from standard input (os.Stdin
):
Scan
: treats new lines as spaces.Scanf
: parses arguments according to a format string.Scanln
: reads one line.
These read from io.Reader
s:
Fscan
Fscanf
Fscanln
These read from an argument string:
Sscan
Sscanf
Sscanln
As you can guess, the following stop at the first new line or EOF:
Scanln
Fscanln
Sscanln
While these treat new lines as spaces:
Scan
Fscan
Sscan
Similar to Printf
we can use format strings with these functions:
Scanf
Fscanf
Sscanf
Scan verbs are the same as Print
. %p
, %T
and # +
flags are not implemented.
Apart from %c
every other verb discards leading whitespace (except new lines).
Let's start by something simple like reading a line from input:
// 02.5-02-scan1.go
package main
import "fmt"
func main() {
var s string
n, err := fmt.Scanln(&s)
if err != nil {
panic(err)
}
fmt.Printf("Entered %d word(s): %s", n, s)
}
All is well when input does not have any whitespace (e.g. space):
$ go run 02.5-02-scan1.go
HelloHello
Entered 1 word(s): HelloHello
But When input has whitespace:
$ go run 02.5-02-scan1.go
Hello Hello
panic: expected newline
goroutine 1 [running]:
main.main()
Z:/Go/src/Hacking-with-Go/code/02/02.5/02.5-02-scan1.go:10 +0x1ae
exit status 2
Scan
does not return the number of characters as we expect from the C equivalent. It returns the number of words entered.Scan
and friends separate words by whitespace. Meaning when we enteredHello Hello
, they are counted as two words.Scanln
stored the firstHello
ins
and was expecting a new line or EOF to finish that. Instead it got a new word and panicked.
If we wanted to just read a number or anything without whitespace, it would have worked.
If we replace Scanln
with Scan
in the code, the program will not panic but will ignore anything after the first whitespace.
Lesson learned Don't use Scan for reading user input with whitespace.
An easier way to read user input is through bufio
readers. We are looking for the quickest ways to get things done after all.
// 02.5-03-bufioreadstring.go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// ReadString will read until first new line
input, err := reader.ReadString('\n') // Need to pass '\n' as char (byte)
if err != nil {
panic(err)
}
fmt.Printf("Entered %s", input)
}
ReadString reads until the first occurrence of its argument (delimiter). The delimiter should be a byte
hence we need to pass a char
using single quotes (\n
). "\n"
is a string and will not work.
bufio.Reader
has more methods for reading different types. For example we can directly read user input and convert it to bytes with ReadBytes.
// 02.5-04-bufioreadbytes.go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Read bytes until the new line
input, err := reader.ReadBytes('\n') // Need to pass '\n' as char (byte)
if err != nil {
panic(err)
}
// Print type of "input" and its value
fmt.Printf("Entered type %T, %v\n", input, input)
// Print bytes as string
fmt.Printf("Print bytes as string with %%s %s", input)
}
We are printing the type of input
and its value as-is first. Then we print the bytes as string with %s
.
$ go run 02.5-04-bufioreadbytes.go
Hello 0123456789
Entered type []uint8, [72 101 108 108 111 32 48 49 50 51 52 53 54 55 56 57 13 10]
Print bytes as string with %s Hello 0123456789
As you can see bytes
are just uint8
(unsigned ints) and printing them yields decimal values and not hex. Don't worry about bytes and strings now. We will have a byte manipulation chapter.
Fast lookup/add/delete. Each key is associated with a value (similar to Python dict).
Declare an initialized map:
mapName := make(map[KeyType]ValueType)
.
KeyType
needs to be a comparable
type. ValueType
can be anything.
If a key does not exist, the result is a zero value. For example 0
for int
.
To check if a key exists or not (0 might be a valid value in the map) use:
value, ok := mapName[key]
If ok
is true then the key exists (and false if the key is not there).
To test for a key without getting the value drop the value like this _, ok := mapName[key]
and then just check ok
.
range
iterates over the contents of a map like arrays/slices. But we get keys instead of indexes. Typically we use the range with a for
:
for key, value := range mapName
We can create a map using data:
m := map[string]int{"key0": 0, "key1": 1}
Delete a key/value pair with delete
:
delete(m, "key0")
// 02.5-05-maps.go
package main
import "fmt"
type intMap map[int]int
// Create a Stringer for this map type
func (i intMap) String() string {
var s string
s += fmt.Sprintf("Map type %T\n", i)
s += fmt.Sprintf("Length: %d\n", len(i))
// Iterate through all key/value pairs
for k, v := range i {
s += fmt.Sprintf("[%v] = %v\n", k, v)
}
return s
}
func main() {
// Create a map
map1 := make(intMap)
// Add key/value pairs
map1[0] = 10
map1[5] = 20
// Print map - Stringer will be called
fmt.Println(map1)
// Map type main.intMap
// Length: 2
// [0] = 10
// [5] = 20
// Delete a key/value pair
delete(map1, 0)
fmt.Println(map1)
// Map type main.intMap
// Length: 1
// [5] = 20
// Create a map on the spot using members
map2 := map[string]string{"key1": "value1", "key2": "value2"}
fmt.Println(map2)
// map[key1:value1 key2:value2]
}