Command-Line Arguments

os.Args: slice. os.Args[0] refers to the program itself, and we often use os.Args[1:].

package main

import (
	"fmt"
	"os"
	"strings"
)
func main() {
    fmt.Println(strings.Join(os.Args[1:], " ")) // "s +" is inefficient
}

Format Output fmt.Printf()

Verbs

VerbMeaningExample
%vPrint the value in its default format
%+vIf printing a struct, it adds the field names.{Name
Age
}
%#vPrints the value as it would be written in Go code.main.User{Name:“Alice”, Age
}
%TThe type of the value.
%%Prints %%
%sString
%qQuoted string”Hello, world”
%xHexadecimal dump of the string’s bytes (lowercase).
%bBinary
%dDecimal
%oOctal
%xHex(Lowercase)
%XHex(Uppercase)
%fDefaults to 6 decimal places.
%.2fRestricts the output to exactly n decimal places.3.14
%gDrops trailing zeros and decides between %f or %e based on size.3.14159
%eScientific notation (exponential).3.141590e+00
%tbooltrue
%ppointer0xc0000b2008

Index Explicitly [n]

Reuses the nn-th argument past the format string. Saves you from typing the same variable over and over.
eg: o:=0666; %[1]o \rightarrow 666

Alternative Format #

Forces the output to include its sharp, recognizable base prefix (0 for octal, 0x for hex).
eg: o:=0666; %#o \rightarrow 0666

Sizing and Padding

You can insert numbers between the % and the verb to control spacing and alignment, which is great for building text-based tables or logs.

fmt.Printf("|%5s|\n", "Go")   // Right-aligned, width 5:  |   Go|
fmt.Printf("|%-5s|\n", "Go")  // Left-aligned, width 5:   |Go   |
fmt.Printf("|%05d|\n", 42)    // Pad with leading zeros:  |00042|

iota

Rules:

  1. Only can be used in const group.
  2. From 0, every line +1.

crypto/sha256

Short Text Encryption

sum := sha256.Sum256([]byte(password))

Return a [32]byte array.

Add salt:

sha256.Sum256([]byte(password + "Random_Salt_#9823"))

Large Files Streaming

package main

import (
	"crypto/sha256"
	"fmt"
	"io"
	"strings"
)

func main() {
	// Emulate a large file source
	largeData := strings.NewReader("Large data 1...Large data 2...")

	// 1. Create a hash writer
	h := sha256.New()

	// 2. Use io.Copy to transmit chunked data to the hash writer
	if _, err := io.Copy(h, largeData); err != nil {
		fmt.Println("Failed to read data:", err)
		return
	}

	// 3. h.Sum(nil) to get final result
	result := h.Sum(nil)

	fmt.Printf("Large files' SHA256 is: %x\n", result)
}

Recursively Adding Data

h := sha256.New()
for {
    buf := make([]byte, 1024)
    n, err := file.Read(buf)
    if n > 0 {
        h.Write(buf[:n])
    }
    if err == io.EOF {
        break
    }
}

finalHash := h.Sum(nil)

eg1: Write a function that counts the number of bits that are different in two SHA256
hashes.

package main

import {
	"fmt"
	"crypto/sha256"
}

func diffBits (s1, s2 []byte) int {
	count := 0
	for i := 0; i < len(s1); i++ {
		xorResult := s1[i] ^ s2[i]
		count += popCount(xorResult)
	}
	return count
}

// A helper function to count '1' bits in a single byte
func popCount(b byte) int {
	bits := 0
	for b > 0 {
		if b&1 == 1 {
			bits++
		}
		b >>= 1 // shift right to check the next bit
	}
	return bits
}

func main() {
	c1 := sha256.Sum256([]byte("x"))
	c2 := sha256.Sum256([]byte("X"))
	fmt.Printf("Different bits: %d\n", diffBits(c1, c2))
}

eg2: Write a program that prints the SHA256 hash of its standard input by default but
supports a command-line flag to print the SHA384 or SHA512 hash instead.

package main

import (
	"crypto/sha256"
	"crypto/sha512"
	"flag"
	"fmt"
	"hash"
	"io"
	"os"
)

func main() {
	// 1. Define the command-line flag
	// -algo is the flag name, "sha256" is the default value
	algoPtr := flag.String("algo", "sha256", "The hash algorithm to use (sha256, sha384, sha512)")

	// 2. Parse the flags from the command line
	flag.Parse()

	// 3. Declare a generic hash interface variable
	var h hash.Hash

	// 4. Select the algorithm based on the user's flag input
	switch *algoPtr {
	case "sha384":
		h = sha512.New384() // SHA384 lives inside the sha512 package
	case "sha512":
		h = sha512.New()
	case "sha256":
		h = sha256.New()
	default:
		// If the user types something invalid, print an error and exit
		fmt.Fprintf(os.Stderr, "Error: unsupported algorithm '%s'\n", *algoPtr)
		os.Exit(1)
	}

	// 5. Pipe standard input (os.Stdin) into our hash object
	// This streams the data efficiently without overloading memory
	if _, err := io.Copy(h, os.Stdin); err != nil {
		fmt.Fprintf(os.Stderr, "Error reading input: %v\n", err)
		os.Exit(1)
	}

	// 6. Calculate and print the final checksum
	// h.Sum(nil) extracts the bytes, and %x formats them into a hex string
	fmt.Printf("%x\n", h.Sum(nil))
}

String & []byte & Rune

In golang, a string is actually a []byte, len() returns the number of bytes not runes. To get runes, use utf8.RuneCountInString().

bytes.Buffer

The data in it is ASCII code.Used when string is frequently extended.

Slice

When delivering a slice to a function, the copied slice also has its pointer pointed to the original array, so any change of the slice in the function will cause the original slice to change.

Maps

K[V]

  • K must be comparable using == , floating number and NaN are bad choices
  • K & V can’t be the same type
    The zero value for a map type is nil.

Basic Operations

Create

ages := make(map[string]int)
ages["alice"] = 31
ages["charlie"] = 34

or

ages := map[string]int {
	"alice": 31,
	"charlie": 34,
}

A empty map: map[string]int{}

Delete

delete(ages, "alice")

Enumerate

Go intentionally to “shuffle” the map, so the result will be different in every run:

for name, age := range ages {
	fmt.Printf("%s\t%d\n", name, age)
}

To enumerate the key/value pairs in order, we must sort
the keys explicitly.

keys := make([]string, 0, len(ages))
for name := range ages {
    keys = append(keys, name)
}
sort.Strings(key)
for _, name := range keys {
    fmt.Printf("%s\t%d\n", name, ages[name])
}

Safety

If the element isn’t in the map, the map will returns the zero value for its type.

And a map element is not a variable, and we cannot take its address:

_ = &ages["bob"] // compile error: cannot take address of map element

Most operations on maps are safe to perform on a nil map reference, but storing to a nil map causes a panic:

ages["carol"] = 21 // panic: assignment to entry in nil map

You must allocate the map before you can store into it.

To distinguish between a nonexistent element and an element that happens to have the value zero:

age, ok := ages["bob"]
if !ok { /* "bob" is not a key in this map; age == 0. */}

Act Like set

// Using a map as a set
registeredUsers := map[string]bool{
    "alice": true,
    "bob":   true,
}

To check one element:

if registeredUsers["alice"] {
    fmt.Println("Alice is registered!")
}

However, not all map[string]bool values are simple sets; some may contain both true and false values. To check a element in this kind of map, run this:

_, ok := userStatus["bob"]

Ref

edu.anarcho-copy.org/Programming Languages/Go/The Go Programming Language - Donovan, Alan A. A. _ Kernigha_6127.pdf