Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Essential Utility Functions in Go

Notes May 9 3

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

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.