Skip to main content

Golang Refresher :: fmt & strings

Guangzhou, China

Environment

Visual Studio Code

Install the Go extension und use the VS Code command line (F1) to go: install/update tools and install everything that is offered:

Go Websockets

The Go Standard Library

fmt :: String formatting and Printing

The fmt Package implements formatted I/O with functions analogous to C's printf and scanf. The format 'verbs' are derived from C's but are simpler.

package main

import "fmt"

func main() {

	// Print a string
	fmt.Print("a string")

	// Print a string with newline
	fmt.Println("a string followed by a newline")

	// Print string with values
	const fortytwo = 42
	const answer = "answer"
	const everything = "everything"
	fmt.Println("The", answer, "to", everything, "is", fortytwo)

	// Print a slice
	items := []int{33, 66, 99, 666}
	length, err := fmt.Println(items)
	fmt.Println(length, err)

}

Run the file with:

go run ./main.go

a stringa string followed by a newline
The answer to everything is 42
[33 66 99 666]
15 <nil>

Printf and Sprintf

Print data with the help of formatting verbs - e.g. :

General:
%vthe value in a default format. when printing structs, the plus flag (%+v) adds field names
%#va Go-syntax representation of the value
%Ta Go-syntax representation of the type of the value
%%a literal percent sign; consumes no value
Boolean:
%tthe word true or false
Integer:
%bbase 2
%cthe character represented by the corresponding Unicode code point
%dbase 10
%obase 8
%Obase 8 with 0o prefix
%qa single-quoted character literal safely escaped with Go syntax.
%xbase 16, with lower-case letters for a-f
%Xbase 16, with upper-case letters for A-F
%UUnicode format: U+1234; same as "U+%04X"
Floating-point and complex constituents:
%bdecimalless scientific notation with exponent a power of two, in the manner of strconv.FormatFloat with the 'b' format, e.g. -123456p-78
%escientific notation, e.g. -1.234456e+78
%Escientific notation, e.g. -1.234456E+78
%fdecimal point but no exponent, e.g. 123.456
%Fsynonym for %f
%g%e for large exponents, %f otherwise. Precision is discussed below.
%G%E for large exponents, %F otherwise
%xhexadecimal notation (with decimal power of two exponent), e.g. -0x1.23abcp+20
%Xupper-case hexadecimal notation, e.g. -0X1.23ABCP+20
String and slice of bytes (treated equivalently with these verbs):
%sthe uninterpreted bytes of the string or slice
%qa double-quoted string safely escaped with Go syntax
%xbase 16, lower-case, two characters per byte
%Xbase 16, upper-case, two characters per byte
Slice:
%paddress of 0th element in base 16 notation, with leading 0x
Pointer:
%pbase 16 notation, with leading 0x

The %b, %d, %o, %x and %X verbs also work with pointers, formatting the value exactly as if it were an integer.

package main

import "fmt"

type circle struct {
	radius int
	border int
}

func main() {

	x := 26
	f := 337.99

	// Formatting
	// decimal
	fmt.Printf("%d\n", x)
	// hexadecimal
	fmt.Printf("%x\n", x)

	// Booleans
	fmt.Printf("%t\n", x > 10)

	// Floats
	fmt.Printf("%f\n", f)
	fmt.Printf("%e\n", f)

	// Explicit argument indexes
	fmt.Printf("%d %d\n", 69, 119)
	fmt.Printf("%[2]d %[1]d\n", 69, 119)

	// Explicit argument indexes with repeated values
	fmt.Printf("%d %d %#[1]o %#[2]x\n", 69, 119)

	// Print in default format
	c := circle {
		radius: 456,
		border: 2,
	}

	fmt.Printf("%v\n", c)
	fmt.Printf("%+v\n", c)
	fmt.Printf("%T\n", c)

	// Use SprintF to return value as string
	s := fmt.Sprintf("%d %d", 69, 119)
	fmt.Println(s)
	fmt.Printf("%T\n", s)

}
go run ./main.go

26
1a
true
337.990000
3.379900e+02
69 119
119 69
69 119 0105 0x77
{456 2}
{radius:456 border:2}
main.circle
69 119
string
package main

import "fmt"

func main() {

	f := 997.123

	// Decimal precision (e.g. 2 decimal points)
	fmt.Printf("%.2f\n", f)

	// Max width (e.g. 15 spaces) and default precision (this will just
	// add empty spaces in front to make lists look pretty)
	fmt.Printf("%15f\n", f)

	// Padding and precision
	fmt.Printf("%15.2f\n", f)

	// Add a character in front, e.g. a `+`
	fmt.Printf("%+15.3f\n", f)

	// Padding with zeros instead of empty spaces
	fmt.Printf("%015.4f\n", f)

}
go run .\formatting.go

997.12
     997.123000
         997.12
       +997.123
0000000997.1230

Stdin

Reading and printing user inputs:

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {

	reader := bufio.NewReader(os.Stdin)
	s, _ := reader.ReadString('\n')
	fmt.Println(s)
	
}
go run .\main.go
hello world
hello world

strings|strconv|unicode :: Manipulating string and text content

Package strings implements simple functions to manipulate UTF-8 encoded strings.

strings - Basics

package main

import (
	"fmt"
	"strings"
)

func main() {

	s:= "This string is never gonna give you up, never gonna let you down..."

	// Print length of a string
	fmt.Println(len(s))

	// Iteration
	for _, ch := range s {
		fmt.Print(string(ch), "-")
	}
	fmt.Println()

	// Operators
	fmt.Println("Sapporro" < "Asahi")
	fmt.Println("Tomcat" > "Hornet")
	fmt.Println("rick" == "Rick")
	fmt.Println("rick" != "Rick")

	// Compare
	compare_01 := strings.Compare("Sapporro", "Asahi")
	fmt.Println(compare_01)
	compare_02 := strings.Compare("Sapporro", "Sapporro")
	fmt.Println(compare_02)

	// Unicode case-folding
	fmt.Println(strings.EqualFold("中國", "台灣"))
	fmt.Println(strings.EqualFold("台灣", "台灣"))

	// Character cases
	s1 := strings.ToUpper(s)
	s2 := strings.ToLower(s)
	s3 := strings.Title(s)
	fmt.Println(s1, s2, s3)

}
go run .\main.go

67
T-h-i-s- -s-t-r-i-n-g- -i-s- -n-e-v-e-r- -g-o-n-n-a- -g-i-v-e- -y-o-u- -u-p-,- -n-e-v-e-r- -g-o-n-n-a- -l-e-t- -y-o-u- -d-o-w-n-.-.-.-       
false
true
false
true
1
0
false
true
THIS STRING IS NEVER GONNA GIVE YOU UP, NEVER GONNA LET YOU DOWN... this string is never gonna give you up, never gonna let you down... This 
String Is Never Gonna Give You Up, Never Gonna Let You Down...
package main

import (
	"fmt"
	"strings"
)

func main() {

	s:= "This string is never gonna give you up, never gonna let you down..."
	vowels := "aeiouAEIOU"

	fname := "textfile.txt"
	fname2 := "imagefile.png"

	// Contains a sub-string
	fmt.Println(strings.Contains(s, "gonna"))

	// ContainsAny of these characters
	fmt.Println(strings.ContainsAny(s, "xyzi"))

	// Find offset of first instance of substring
	fmt.Println(strings.Index(s, "give"))
	fmt.Println(strings.Index(s, "rick"))

	// Find offset of first instance of any of a set of characters
	fmt.Println(strings.IndexAny(s, vowels))
	fmt.Println(strings.IndexAny("xyzs", vowels))

	// Check if strings starts/ends with a substring
	fmt.Println(strings.HasSuffix(fname, "txt"))
	fmt.Println(strings.HasPrefix(fname2, "image"))

	// Count non-overlapping instances of a substring
	fmt.Println(strings.Count(s, "gonna"))
	fmt.Println(strings.Count(s, "is"))

}
go run .\main.go
true
true
27
-1
2
-1
true
true
2
2

strings - Manipulations

package main

import (
	"fmt"
	"strings"
	"unicode"
)

func main() {

	s1 := "This string is never gonna give you up"
	s2 := []string{"one", "two", "three", "four"}
	s3 := "Never gonna give, never gonna give. Give you up!"

	// Split into substrings
	sub1 := strings.Split(s1, "never")
	fmt.Printf("%q\n", sub1)

	// Split string around whitespaces
	result := strings.Fields(s1)
	fmt.Printf("%q\n", result)

	// Split around punctuation
	f := func(c rune) bool {
		return unicode.IsPunct(c)
	}
	result2 := strings.FieldsFunc(s3, f)
	fmt.Printf("%q\n", result2)

	// Join substrings
	result3 := strings.Join(s2, "-")
	fmt.Println(result3)

	// Replace substring
	rep := strings.NewReplacer(",", " |", ".", " |", "!", " |")
	result4 := rep.Replace(s3)
	fmt.Println(result4)
}
go run .\main.go

["This string is " " gonna give you up"]
["This" "string" "is" "never" "gonna" "give" "you" "up"]
["Never gonna give" " never gonna give" " Give you up"]
one-two-three-four
Never gonna give | never gonna give | Give you up |

strings - Mapping

package main

import (
	"fmt"
	"strings"
)

func main() {
	// Become a secret agent by encoding your messages
	s:= "This string is never gonna give you up, never gonna let you down..."
	// As a cypher push every character `+2`
	shift := 2

	// Mapping function
	transform := func (r rune) rune {
		switch {

		// if character is uppercase
		case r >= 'A' && r <= 'Z':
			// get ASCII base code of character and add value of `shift`
			value := int('A') + (int(r) - int('A') + shift)
			// if you hit 91 (which equals 'Z')
			if value > 91 {
				// subtract 26 to start from the beginning 'A'
				value -= 26
			// if you hit the lower threshold
			} else if value < 65 {
				// add 26
				value += 26
			}
			return rune(value)
			
		// if character is lowercase
		case r >= 'a' && r <= 'z':
			// get base code of character and add value of `shift`
			value := int('a') + (int(r) - int('a') + shift)
			// if you hit 91 (which equals 'z')
			if value > 122 {
				// subtract 26 to start from the beginning 'a'
				value -= 26
			// if you hit the lower threshold
			} else if value < 97 {
				// add 26
				value += 26
			}
			return rune(value)
		}
		return r
	}

	// Encode message
	encode := strings.Map(transform, s)
	fmt.Println(encode)

	// Encode message
	shift = -shift
	decode := strings.Map(transform, encode)
	fmt.Println(decode)

}
go run .\main.go

Vjku uvtkpi ku pgxgt iqppc ikxg aqw wr, pgxgt iqppc ngv aqw fqyp...
This string is never gonna give you up, never gonna let you down...

strings - Builder

package main

import (
	"fmt"
	"strings"
)

func main() {
	
	var sb strings.Builder

	// Provide content
	sb.WriteString("This string is never gonna give you up \n")
	sb.WriteString("Never gonna let you down \n")
	sb.WriteString("Never gonna say goodbye \n")
	sb.WriteString("Never gonna tell a lie and hurt you \n")

	// Concatenate string
	fmt.Println(sb.String())

	// Get length of build string
	fmt.Println("Builder size:", sb.Len())

	// Builder capacity
	fmt.Println("Capacity:", sb.Cap())

	// Add capacity of 1k to reserve space
	sb.Grow(1024)
	fmt.Println("Capacity:", sb.Cap())

	// Reset content of builder
	sb.Reset()
	fmt.Println("After reset")
	fmt.Println("Capacity:", sb.Cap())
	fmt.Println("Builder Size:", sb.Len())
}
go run .\main.go
This string is never gonna give you up 
Never gonna let you down
Never gonna say goodbye
Never gonna tell a lie and hurt you

Builder size: 128
Capacity: 192
Capacity: 1408
After reset
Capacity: 0
Builder Size: 0

strconv - Parse

Package strconv implements conversions to and from string representations of basic data types.

package main

import (
	"fmt"
	"strconv"
)

func main() {

	str := "101"
	num := 100


	// Converting int into string
	// The string function does NOT convert an int into a string!
	newstr1 := string(str)
	fmt.Println("The first string is:", newstr1)
	fmt.Printf("%T\n", newstr1)
	newstr2 := string(num)
	fmt.Println("This is not the string I expected:", newstr2)
	fmt.Printf("%T\n", newstr2)
	// To transform the int use:
	newstr3 := strconv.Itoa(num)
	fmt.Println("The second string is:", newstr3)
	fmt.Printf("%T\n", newstr3)


	// Convert string to int
	newstr4, err := strconv.Atoi(str)
	if (err != nil) {
		fmt.Println(err.Error())
	}
	fmt.Println("This is now an integer:", newstr4)
	fmt.Printf("%T\n", newstr4)


	// Parsing strings into different data types
	b, _ := strconv.ParseBool("true")
	fmt.Println(b)
	fmt.Printf("%T\n", b)
	f, _ := strconv.ParseFloat("3.14159", 64)
	fmt.Println(f)
	fmt.Printf("%T\n", f)
	i, _ := strconv.ParseInt("-42", 10, 64)
	fmt.Println(i)
	fmt.Printf("%T\n", i)
	u, _ := strconv.ParseUint("42", 10, 64)
	fmt.Println(u)
	fmt.Printf("%T\n", u)

	// Format converts values into strings
	s1 := strconv.FormatBool(true)
	fmt.Println(s1)
	fmt.Printf("%T\n", s1)
	s2 := strconv.FormatFloat(3.1415, 'E', -1, 64)
	fmt.Println(s2)
	fmt.Printf("%T\n", s2)
	s3 := strconv.FormatInt(-42, 10)
	fmt.Println(s3)
	fmt.Printf("%T\n", s3)
	s4 := strconv.FormatUint(42, 10)
	fmt.Println(s4)
	fmt.Printf("%T\n", s4)
}
go run .\main.go

The first string is: 101
string
This is not the string I expected: d
string
The second string is: 100
string
This is now an integer: 101
int
true
bool
3.14159
float64
-42
int64
42
uint64
true
string
3.1415E+00
string
-42
string
42
string

unicode

Package unicode provides data and functions to test some properties of Unicode code points.

package main

import (
	"fmt"
	"unicode"
)

func main() {

	const s = "This 'string' is *never* gonna give you up, *never* gonna let you down..."

	punctCount := 0
	lowerCount, upperCount := 0, 0
	spaceCount := 0
	hexdigitCount :=0

	// Count unicode characters:
	for _, ch := range s {
		if unicode.IsPunct(ch) {
			punctCount++
		}
		if unicode.IsLower(ch) {
			lowerCount++
		}
		if unicode.IsUpper(ch) {
			upperCount++
		}
		if unicode.IsSpace(ch) {
			spaceCount++
		}
		if unicode.Is(unicode.Hex_Digit, ch) {
			hexdigitCount++
		}
	}

	fmt.Println("Punctuations", punctCount)
	fmt.Println("Lower Chars", lowerCount)
	fmt.Println("Upper Chars", upperCount)
	fmt.Println("Space Chars", spaceCount)
	fmt.Println("HexDigital Chars", hexdigitCount)
}
go run .\main.go

Punctuations 10
Lower Chars 50
Upper Chars 1
Space Chars 12
HexDigital Chars 9