Essential Utility Functions in Go
Time Formatting
Go uses reference time Mon Jan 2 15:04:05 MST 2006 to format and parse dates. The numeric value 2006 is not a placeholder but the actual reference year.
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
// RFC3339 is commonly used for timestamps
fmt.Println(now.Format(time.RFC3339))
// Parse a timestamp string
parsed, err := time.Parse(time.RFC3339, "2012-11-01T22:08:41+00:00")
if err == nil {
fmt.Println(parsed)
}
// Custom format patterns
fmt.Println(now.Format("3:04PM"))
fmt.Println(now.Format("Mon Jan _2 15:04:05 2006"))
fmt.Println(now.Format("2006-01-02T15:04:05.999999-07:00"))
// Manual construction using components
fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n",
now.Year(), now.Month(), now.Day(),
now.Hour(), now.Minute(), now.Second())
// Parsing error demonstration
_, err = time.Parse("Mon Jan _2 15:04:05 2006", "8:41PM")
fmt.Println(err)
}
Output:
2017-03-23T11:41:52+08:00
2012-11-01 22:08:41 +0000 +0000
11:41AM
Thu Mar 23 11:41:52 2017
2017-03-23T11:41:52.246508+08:00
2017-03-23T11:41:52-00:00
parsing time "8:41PM" as "Mon Jan _2 15:04:05 2006": cannot parse "8:41PM" as "Mon"
String Formatting
The fmt package provides comprehansive verbs for formatting values.
package main
import (
"fmt"
"os"
)
type coord struct {
x, y int
}
func main() {
p := coord{1, 2}
// Basic formatting
fmt.Printf("%v\n", p) // {1 2}
fmt.Printf("%+v\n", p) // {x:1 y:2}
fmt.Printf("%#v\n", p) // main.coord{x:1, y:2}
fmt.Printf("%T\n", p) // main.coord
fmt.Printf("%t\n", true) // true
// Number formatting
fmt.Printf("%d\n", 123) // decimal
fmt.Printf("%b\n", 14) // binary
fmt.Printf("%c\n", 33) // character
fmt.Printf("%x\n", 456) // hexadecimal
// Floating point
fmt.Printf("%f\n", 78.9) // float
fmt.Printf("%e\n", 123400000.0) // scientific
fmt.Printf("%E\n", 123400000.0) // scientific uppercase
// String formatting
fmt.Printf("%s\n", "\"string\"") // raw string
fmt.Printf("%q\n", "\"string\"") // quoted string
fmt.Printf("%x\n", "hex this") // hex encoding
// Pointer
fmt.Printf("%p\n", &p) // pointer address
// Width and precision
fmt.Printf("|%6d|%6d|\n", 12, 345)
fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45)
fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)
fmt.Printf("|%6s|%6s|\n", "foo", "b")
fmt.Printf("|%-6s|%-6s|\n", "foo", "b")
// Sprintf and Fprintf
result := fmt.Sprintf("a %s", "string")
fmt.Println(result)
fmt.Fprintf(os.Stderr, "an %s\n", "error")
}
Output:
{1 2}
{x:1 y:2}
main.coord{x:1, y:2}
main.coord
true
123
1110
!
1c8
78.900000
1.234000e+08
1.234000E+08
"string"
"\"string\""
6865782074686973
0xc042004280
| 12| 345|
| 1.20| 3.45|
|1.20 |3.45 |
| foo| b|
|foo |b |
a string
an error
Regular Expressions
The regexp package offers pattern matching and manipulation capabilities.
package main
import (
"bytes"
"fmt"
"regexp"
)
func main() {
// Quick match test using string pattern
matched, _ := regexp.MatchString("p([a-z]+)ch", "peach")
fmt.Println(matched)
// Compile for repeated use
pattern, _ := regexp.Compile("p([a-z]+)ch")
// Check if pattern matches
fmt.Println(pattern.MatchString("peach"))
// Find first match
fmt.Println(pattern.FindString("peach punch"))
// Find match with index positions
fmt.Println(pattern.FindStringIndex("peach punch"))
// Find complete match plus submatches
fmt.Println(pattern.FindStringSubmatch("peach punch"))
// Index positions for match and submatches
fmt.Println(pattern.FindStringSubmatchIndex("peach punch"))
// Find all matches
fmt.Println(pattern.FindAllString("peach punch pinch", -1))
// Index positions for all matches and submatches
fmt.Println(pattern.FindAllStringSubmatchIndex("peach punch pinch", -1))
// Limit number of matches
fmt.Println(pattern.FindAllString("peach punch pinch", 2))
// Use with byte slice
fmt.Println(pattern.Match([]byte("peach")))
// MustCompile for constants (panics on invalid pattern)
expr := regexp.MustCompile("p([a-z]+)ch")
fmt.Println(expr)
// Replace matched text
fmt.Println(pattern.ReplaceAllString("a peach", "<fruit>"))
// Transform matches with function
input := []byte("a peach")
transformed := pattern.ReplaceAllFunc(input, bytes.ToUpper)
fmt.Println(string(transformed))
}
Output:
true
true
peach
[0 5]
[peach ea]
[0 5 1 3]
[peach punch pinch]
[[0 5 1 3] [6 11 7 9] [12 17 13 15]]
[peach punch]
true
p([a-z]+)ch
a <fruit>
a PEACH
JSON Encoding and Decoding
The encoding/json package handles conversion between Go values and JSON.
package main
import (
"encoding/json"
"fmt"
"os"
)
type apiResponse struct {
Page int
Fruits []string
}
type filteredResponse struct {
Page int `json:"page"`
Fruits []string `json:"fruits"`
}
func main() {
// Primitive types
fmt.Println(string(json.Marshal(true)))
fmt.Println(string(json.Marshal(1)))
fmt.Println(string(json.Marshal(2.34)))
fmt.Println(string(json.Marshal("gopher")))
// Slices and maps
fruits := []string{"apple", "peach", "pear"}
fmt.Println(string(json.Marshal(fruits)))
inventory := map[string]int{"apple": 5, "lettuce": 7}
fmt.Println(string(json.Marshal(inventory)))
// Structs without tags - uses field names
res1 := &apiResponse{Page: 1, Fruits: []string{"apple", "peach", "pear"}}
fmt.Println(string(json.Marshal(res1)))
// Structs with tags - uses specified keys
res2 := &filteredResponse{Page: 1, Fruits: []string{"apple", "peach", "pear"}}
fmt.Println(string(json.Marshal(res2)))
// Decode into interface{}
rawJSON := []byte(`{"num":6.13,"strs":["a","b"]}`)
var data map[string]interface{}
if err := json.Unmarshal(rawJSON, &data); err != nil {
panic(err)
}
fmt.Println(data)
// Type assertions for decoded values
num := data["num"].(float64)
fmt.Println(num)
strs := data["strs"].([]interface{})
fmt.Println(strs[0].(string))
// Decode into typed struct
jsonStr := `{"page": 1, "fruits": ["apple", "peach"]}`
var typedResult filteredResponse
json.Unmarshal([]byte(jsonStr), &typedResult)
fmt.Println(typedResult)
fmt.Println(typedResult.Fruits[0])
// Stream to any io.Writer
encoder := json.NewEncoder(os.Stdout)
encoder.Encode(map[string]int{"apple": 5, "lettuce": 7})
}
Output:
true
1
2.34
"gopher"
["apple","peach","pear"]
{"apple":5,"lettuce":7}
{"Page":1,"Fruits":["apple","peach","pear"]}
{"page":1,"fruits":["apple","peach","pear"]}
map[num:6.13 strs:[a b]]
6.13
a
{1 [apple peach]}
apple
{"apple":5,"lettuce":7}
Number Parsing
The strconv package converts strings to numeric types.
package main
import (
"fmt"
"strconv"
)
func main() {
// ParseFloat with precision bits
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f)
// ParseInt: base 0 auto-detects, 64 requires fit in 64 bits
i, _ := strconv.ParseInt("123", 0, 64)
fmt.Println(i)
// Recognizes hex notation
h, _ := strconv.ParseInt("0x1c8", 0, 64)
fmt.Println(h)
// ParseUint for unsigned integers
u, _ := strconv.ParseUint("789", 0, 64)
fmt.Println(u)
// Atoi for base-10 int
k, _ := strconv.Atoi("135")
fmt.Println(k)
// Errors on invalid input
_, err := strconv.Atoi("wat")
fmt.Println(err)
}
Output:
1.234
123
456
789
135
strconv.ParseInt: parsing "wat": invalid syntax
URL Parsing
The net/url package parses and manipluates URLs.
package main
import (
"fmt"
"net"
"net/url"
)
func main() {
connection := "postgres://user:pass@host.com:5432/path?k=v#f"
parsed, err := url.Parse(connection)
if err != nil {
panic(err)
}
fmt.Println(parsed.Scheme)
fmt.Println(parsed.User)
fmt.Println(parsed.User.Username())
password, _ := parsed.User.Password()
fmt.Println(password)
fmt.Println(parsed.Host)
host, port, _ := net.SplitHostPort(parsed.Host)
fmt.Println(host)
fmt.Println(port)
fmt.Println(parsed.Path)
fmt.Println(parsed.Fragment)
fmt.Println(parsed.RawQuery)
queryParams, _ := url.ParseQuery(parsed.RawQuery)
fmt.Println(queryParams)
fmt.Println(queryParams["k"][0])
}
Output:
postgres
user:pass
user
pass
host.com:5432
host.com
5432
/path
f
k=v
map[k:[v]]
v
SHA1 Hashing
The crypto/sha1 package generates SHA1 checksums.
package main
import (
"crypto/sha1"
"fmt"
)
func main() {
input := "sha1 this string"
// Create hash instance
hasher := sha1.New()
// Feed bytes into hasher
hasher.Write([]byte(input))
// Get final hash sum
hashBytes := hasher.Sum(nil)
fmt.Println(input)
fmt.Printf("%x\n", hashBytes)
}
Output:
sha1 this string
cf23df2207d99a74fbe169e3eba035e633b65d94
Base64 Encoding
The encoding/base64 package provides Base64 conversion.
package main
import (
b64 "encoding/base64"
"fmt"
)
func main() {
data := "abc123!?$*&()'-=@~"
// Standard encoding
encoded := b64.StdEncoding.EncodeToString([]byte(data))
fmt.Println(encoded)
decoded, _ := b64.StdEncoding.DecodeString(encoded)
fmt.Println(string(decoded))
fmt.Println()
// URL-safe encoding
urlEncoded := b64.URLEncoding.EncodeToString([]byte(data))
fmt.Println(urlEncoded)
urlDecoded, _ := b64.URLEncoding.DecodeString(urlEncoded)
fmt.Println(string(urlDecoded))
}
Output:
YWJjMTIzIT8kKiYoKSctPUB+
abc123!?$*&()'-=@~
YWJjMTIzIT8kKiYoKSctPUB-
abc123!?$*&()'-=@~
File Operations
Reading Files
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"os"
)
func handle(err error) {
if err != nil {
panic(err)
}
}
func main() {
// Read entire file into memory
content, err := ioutil.ReadFile("/tmp/dat")
handle(err)
fmt.Print(string(content))
// Open file for detailed operations
file, err := os.Open("/tmp/dat")
handle(err)
// Read exact number of bytes
buf := make([]byte, 5)
n, err := file.Read(buf)
handle(err)
fmt.Printf("%d bytes: %s\n", n, string(buf))
// Seek to position
offset, err := file.Seek(6, 0)
handle(err)
partial := make([]byte, 2)
read, err := file.Read(partial)
handle(err)
fmt.Printf("%d bytes @ %d: %s\n", read, offset, string(partial))
// Read with minimum bytes requirement
_, err = file.Seek(6, 0)
handle(err)
minBuf := make([]byte, 2)
count, err := io.ReadAtLeast(file, minBuf, 2)
handle(err)
fmt.Printf("%d bytes @ %d: %s\n", count, 6, string(minBuf))
// Reset position
_, err = file.Seek(0, 0)
handle(err)
// Buffered reading with Peek
reader := bufio.NewReader(file)
peeked, err := reader.Peek(5)
handle(err)
fmt.Printf("5 bytes: %s\n", string(peeked))
file.Close()
}
Output:
abcdfawef!@
cawfe
awefawef
awefaf
5 bytes: abcdf
2 bytes @ 6: we
2 bytes @ 6: we
5 bytes: abcdf
Writing Files
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
)
func handle(err error) {
if err != nil {
panic(err)
}
}
func main() {
// Quick write with ioutil
data1 := []byte("hello\ngo\n")
err := ioutil.WriteFile("dat1.txt", data1, 0644)
handle(err)
// Create and write manually
f, err := os.Create("dat2.txt")
handle(err)
defer f.Close()
// Write byte slice
data2 := []byte{115, 111, 109, 101, 10}
written, err := f.Write(data2)
handle(err)
fmt.Printf("wrote %d bytes\n", written)
// Write string
written, err = f.WriteString("writes\n")
fmt.Printf("wrote %d bytes\n", written)
f.Sync()
// Buffered writer for efficiency
buffered := bufio.NewWriter(f)
written, err = buffered.WriteString("buffered\n")
fmt.Printf("wrote %d bytes\n", written)
buffered.Flush()
}
Line Filter
A line filter reads from stdin, transforms, and writes to stdout.
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
upper := strings.ToUpper(scanner.Text())
fmt.Println(upper)
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
}
Environment Variables
The os package provides access to enviroment variables.
package main
import (
"fmt"
"os"
"strings"
)
func main() {
// Set and retrieve variables
os.Setenv("FOO", "1")
fmt.Println("FOO:", os.Getenv("FOO"))
fmt.Println("BAR:", os.Getenv("BAR"))
// List all environment variables
fmt.Println()
for _, env := range os.Environ() {
parts := strings.Split(env, "=")
fmt.Println(parts[0])
}
}
Signal Handling
Signals are communicated through channels in Go.
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
// Channel for signals
signals := make(chan os.Signal, 1)
done := make(chan bool, 1)
// Register for specific signals
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
// Goroutine to handle signals
go func() {
sig := <-signals
fmt.Println()
fmt.Println(sig)
done <- true
}()
fmt.Println("awaiting signal")
<-done
fmt.Println("exiting")
}
Output:
awaiting signal
interrupt
exiting