In this chapter, I'm sharing the string manipulation cheatsheet in part I. to keep on hand for reference when working with strings, which you will be doing a lot of when it comes to automating day-to-day work. Then, you'll find a few useful projects to help flex your string manipulation learnings in part II.
Note: Here's the GitHub for the Project Section after the Cheat Sheet
I. String Manipulation cheatsheet
Print this out, tape it by your desk, or put it in your favorite three-ring binder for school. It contains almost every string manipulation with an expression as an example for when you're working.
String Literals
Expression |
Result |
Comment |
" " |
|
Empty string is default zero value for the string type |
Japan 日本 |
Japan 日本 |
Go has UTF-8 built in |
\\ |
\ |
Backslash |
\n |
|
newline |
\t |
|
tab |
Concatenate
Expression |
Result |
Comment |
"Go"+"Lang" |
Golang |
concatenation |
Equal and Compare
Expression |
Result |
Comment |
"Go" == "Go" |
true |
Equality |
strings.EqualFold("GoLang") |
GOLANG |
true unicode case folding |
"Go" < "go" |
true |
Lexicographic order |
Length in bytes or runes
Expression |
Result |
Comment |
len("行く") |
6 |
Length in Bytes |
utf8.RuneCountInString("行く") |
2 |
in runes |
utf8.ValidString("行く") |
true |
UTF-8 |
Index, substring, iterate
Expression |
Result |
Comment |
"GO"[1] |
O |
byte position at 1 |
"GoLang"[1:3] |
oL |
byte indexing |
"GO"[:2] |
GO |
byte indexing |
"GO"[1:] |
O |
byte indexing |
Search
Expression |
Result |
Comment |
strings.Contains("GoLang", "abc") |
false |
Is "abc" in GoLang |
strings.Contains("GoLang","abc")
| true |
Is 'a' or 'b' or 'c' in Golang |
strings.Count("Orange","an")
| 1 |
non-overlapping instances of range |
strings.HasPrefix("GoLang", "Go") |
true |
Does GoLang start with 'Go' |
strings.HasSuffix("GoLang", "Lang") |
true |
Does GoLang end with 'Lang' |
strings.Index("GoLang", "abc") |
-3 |
Index of first abc |
strings.IndexAny("GoLang", "abc") |
3 |
a or b or c |
strings.LastIndex("GoLang", "abc") |
-3 |
Index of last abc |
strings.LastIndexAny("GoLang", "abc") |
3 |
a or b or c |
Replace
Expression |
Result |
Comment |
strings.Replace("foo", "o", ".",2) |
f.. |
Replace first two “o” with “.” |
strings.ToUpper("golang") |
GOLANG |
Uppercase |
strings.ToLower("GoLang") |
golang |
Lowercase |
strings.Title("go lang") |
Go Lang |
Initial letters to uppercase |
strings.TrimSpace(" foo\n") |
foo |
Strip leading and trailing white space |
strings.Trim("foo", "fo") |
|
Strip leading and trailing f:s and o:s |
strings.TrimLeft("foo", "f") |
oo |
only leading |
strings.TrimRight("foo", "o") |
f |
only trailing |
strings.TrimPrefix("foo", "fo") |
o |
strings.TrimSuffix("foo", "o") |
Split by space or comma
Expression |
Result |
Comment |
strings.Fields(" a\t b\n") |
["a" "b"] |
Remove white space |
strings.Split("a,b", ",") |
["a" "b"] |
Remove separator |
strings.SplitAfter("a,b",",") |
["a," "b"] |
Keep separator |
Join strings with a Separator
Expression |
Result |
Comment |
strings.Join([]string{"a","b"}, ":") |
a:b |
Add ':' as separator |
strings.Repeat("da", 2) |
dada |
2 copies of “da” |
Expression, |
Result, |
Comment |
strconv.Itoa(-42), |
-42 |
Int to string |
strconv.FormatInt(255, 16), |
ff |
Base 16 |
Regular Expressions
Expression, |
Meaning |
., |
any character |
[ab], |
the character a or b |
[^ab], |
any character except a or b |
[a-z], |
any character from a to z |
[a-z0-9], |
any character from a to z or 0 to 9 |
\d, |
a digit: [0-9] |
\D, |
a non-digit: [^0-9] |
\s, |
a whitespace character: [\t\n\f\r ] |
\S, |
a non-whitespace character: [^\t\n\f\r ] |
\w, |
a word character: [0-9A-Za-z_] |
\W, |
a non-word character: [^0-9A-Za-z_] |
\p{Greek}, |
Unicode character class* |
\pN, |
one-letter name |
\P{Greek}, |
negated Unicode character class* |
\PN, |
one-letter name |
Regexp, |
Meaning |
x*, |
zero or more x prefer more |
x*?, |
prefer fewer (non-greedy) |
x+, |
one or more x prefer more |
x+?, |
prefer fewer (non-greedy) |
x?, |
zero or one x prefer one |
x??, |
prefer zero |
x{n}, |
exactly n x |
Regexp, |
Meaning |
xy, |
x followed by y |
x|y, |
x or y prefer x |
xy|z, |
same as (xy)|z |
xy*, |
same as x(y*) |
Symbol, |
Matches |
\A, |
at beginning of text |
^, |
at beginning of text or line |
$, |
at end of text |
\z, |
|
\b, |
at ASCII word boundary |
\B, |
not at ASCII word boundary |
First Match
re := regexp.MustCompile(`foo.?`)
fmt.Printf("%q\n", re.FindString("seafood fool")) // "food"
fmt.Printf("%q\n", re.FindString("meat")) // ""
Location
re := regexp.MustCompile(`ab?`)
fmt.Println(re.FindStringIndex("tablett")) // [1 3]
fmt.Println(re.FindStringIndex("foo") == nil) // true
All Matches
re := regexp.MustCompile(`a.`)
fmt.Printf("%q\n", re.FindAllString("paranormal", -1)) // ["ar" "an" "al"]
fmt.Printf("%q\n", re.FindAllString("paranormal", 2)) // ["ar" "an"]
fmt.Printf("%q\n", re.FindAllString("graal", -1)) // ["aa"]
fmt.Printf("%q\n", re.FindAllString("none", -1)) // [] (nil slice)
Replace
re := regexp.MustCompile(`ab*`)
fmt.Printf("%q\n", re.ReplaceAllString("-a-abb-", "T")) // "-T-T-"
Split
a := regexp.MustCompile(`a`)
fmt.Printf("%q\n", a.Split("banana", -1)) // ["b" "n" "n" ""]
fmt.Printf("%q\n", a.Split("banana", 0)) // [] (nil slice)
fmt.Printf("%q\n", a.Split("banana", 1)) // ["banana"]
fmt.Printf("%q\n", a.Split("banana", 2)) // ["b" "nana"]
zp := regexp.MustCompile(`z+`)
fmt.Printf("%q\n", zp.Split("pizza", -1)) // ["pi" "a"]
fmt.Printf("%q\n", zp.Split("pizza", 0)) // [] (nil slice)
fmt.Printf("%q\n", zp.Split("pizza", 1)) // ["pizza"]
fmt.Printf("%q\n", zp.Split("pizza", 2)) // ["pi" "a"]
II. Projects
Regex E-Mail finder from clipboard
In this project, we'll use Regex to determine if a piece of text copied to our clipboard contains an e-mail. The pattern of finding an e-mail in a string of text is handy as you'll probably receive data where it will be useful to the group based on a common characteristic.
If you're interested in building a web application with sign-up / sign-in input fields based on e-mails, this can be useful for determining whether the input is of the correct form.
Note: There is a third-party package dependency in this project, but I trust you remember how to handle that requirement hint go mod init & go get
package main
import (
"fmt"
"regexp"
"strings"
"github.com/atotto/clipboard"
)
func main() {
// create email regexp
regMail, _ := regexp.Compile(`[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}`)
// read os buffer
// find email regexp
text, _ := clipboard.ReadAll()
var mailAddr []string
// found e-mail
if regMail.MatchString(text) {
mailAddr = regMail.FindAllString(text, -1)
}
// Print found e-mails on the terminal
if len(mailAddr) > 0 {
clipboard.WriteAll(strings.Join(mailAddr, "\n"))
fmt.Println("Copied to clipboard:")
fmt.Println(strings.Join(mailAddr, "\n"))
} else {
fmt.Println("No email addresses found.")
}
}
Regex Password Complexity Checker
Password complexity is an additional feature you will want to have for an application. This is because simple passwords are easier for cyber criminals to obtain/guess.
Hence, it is wise to enforce a password complexity of at least 11 characters with some capital letters and numbers thrown in. So, let's build a small program to check whether or not a given password passes the complexity requirement.
package main
import (
"fmt"
"os"
"regexp"
"flag"
)
func main() {
if len(os.Args) < 2 {
fmt.Printf("Usage: %s -h\n", os.Args[0])
} else {
pass := flag.String("p", "", "get password")
flag.Parse()
regStr, _ := regexp.Compile(`([0-9a-zA-Z]){11,}`)
if regStr.MatchString(*pass) {
fmt.Println("Password ok")
} else {
fmt.Println("Bad password")
}
os.Exit(0)
}
}
Running the program and passing in your possible password should result in either a Password OK or Bad Password response from the program:
t@m1 regexppass % go run main.go --p=23498aosethuaosthAT
Pass ok
t@m1 regexppass % go run main.go --p=2Aoeue
Bad password
t@m1 regexppass % go run main.go --p=2AoeueEEEE
Bad password
Quiz Builder
Now, this project is the first in our queue at automating a mundane work task--your teacher friends will love you!
In this project, you'll build a quiz generator around the U.S. and its capitals. However, you will generally have the format of a quiz-building piece of software that can be changed to generate different sorts of quizzes. Maybe, you'll change it to do the capitals of your home country and its states.
package main
import (
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
capitals := map[string]string{
"Alabama": "Montgomery",
"Alaska": "Juneau",
"Arizona": "Phoenix",
"Arkansas": "Little Rock",
"California": "Sacramento",
"Colorado": "Denver",
"Connecticut": "Hartford",
"Delaware": "Dover",
"Florida": "Tallahassee",
"Georgia": "Atlanta",
"Hawaii": "Honolulu",
"Idaho": "Boise",
"Illinois": "Springfield",
"Indiana": "Indianapolis",
"Iowa": "Des Moines",
"Kansas": "Topeka",
"Kentucky": "Frankfort",
"Louisiana": "Baton Rouge",
"Maine": "Augusta",
"Maryland": "Annapolis",
"Massachusetts": "Boston",
"Michigan": "Lansing",
"Minnesota": "Saint Paul",
"Mississippi": "Jackson",
"Missouri": "Jefferson City",
"Montana": "Helena",
"Nebraska": "Lincoln",
"Nevada": "Carson City",
"New Hampshire": "Concord",
"New Jersey": "Trenton",
"New Mexico": "Santa Fe",
"New York": "Albany",
"North Carolina": "Raleigh",
"North Dakota": "Bismarck",
"Ohio": "Columbus",
"Oklahoma": "Oklahoma City",
"Oregon": "Salem",
"Pennsylvania": "Harrisburg",
"Rhode Island": "Providence",
"South Carolina": "Columbia",
"South Dakota": "Pierre",
"Tennessee": "Nashville",
"Texas": "Austin",
"Utah": "Salt Lake City",
"Vermont": "Montpelier",
"Virginia": "Richmond",
"Washington": "Olympia",
"West Virginia": "Charleston",
"Wisconsin": "Madison",
"Wyoming": "Cheyenne",
}
var states, capitalsItems []string
for y, x := range capitals {
capitalsItems = append(capitalsItems, x)
states = append(states, y)
}
for i := 0; i < 35; i++ {
// сreate the quiz text file
str1 := "quiz_" + strconv.Itoa(i+1) + ".txt"
quizFile, err := os.Create(str1)
check(err)
defer quizFile.Close()
// create the answer key to the quiz
str2 := "answer_key_" + strconv.Itoa(i+1) + ".txt"
answerKeyFile, err := os.Create(str2)
check(err)
defer answerKeyFile.Close()
// Create portion for students to fill out
quizFile.WriteString("Student Number:\n\nName:\n\nDate:\n\n")
str3 := "Quiz " + strconv.Itoa(i+1)
quizFile.WriteString(strings.Repeat(" ", 20) + str3)
quizFile.WriteString("\n\n")
rand.Seed(time.Now().UnixNano())
// mix of the States
shuffle(states)
// Iterate through and build the question out
for j := 0; j < 50; j++ {
correctAnswer := capitals[states[j]]
wrongAnswers := make([]string, len(capitalsItems))
copy(wrongAnswers, capitalsItems)
// shuffle wrong answers
answNoCorrect := make([]string, len(wrongAnswers)-1)
for l := 0; l < len(wrongAnswers); l++ {
if wrongAnswers[l] == correctAnswer {
copy(answNoCorrect, removeAtIndex(wrongAnswers, l))
}
}
// create answer options A-D
var answerOptions []string
for l := 0; l < 3; l++ {
answerOptions = append(answerOptions, answNoCorrect[l])
}
answerOptions = append(answerOptions, correctAnswer)
shuffle(answerOptions)
// create question
str3 := strconv.Itoa(j+1) + " What is the Capital of " + states[j] + "?" + "\n"
quizFile.WriteString(str3)
strAbcd := "ABCD"
for l := 0; l < 4; l++ {
strAnsw := string(strAbcd[l]) + ". " + answerOptions[l] + "\n"
quizFile.WriteString(strAnsw)
}
// make quiz and save it
quizFile.WriteString("\n")
// make answer key and save it
strAnswerOk := ""
for l := 0; l < len(answerOptions); l++ {
if answerOptions[l] == correctAnswer {
strAnswerOk += string(strAbcd[l])
}
}
strCorAnsw := strconv.Itoa(j+1) + ". " + strAnswerOk + "\n"
answerKeyFile.WriteString(strCorAnsw)
}
}
}
// helper functions for making quiz building easier
func check(e error) {
if e != nil {
panic(e)
}
}
func shuffle(a []string) {
for i := range a {
j := rand.Intn(i + 1)
a[i], a[j] = a[j], a[i]
}
}
func removeAtIndex(source []string, index int) []string {
lastIndex := len(source) - 1
source[index], source[lastIndex] = source[lastIndex], source[index]
return source[:lastIndex]
}
There you have it, the foundation of all the string knowledge you'll use to work with string data in files, clipboards, or anywhere else.
Chapter 7. String Manipulation