Ch. 7 String Manipulation
Tags: go, beginners, books, programming
Published: 2023-04-13 16:10:20

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](https://github.com/toul-codes/automate-the-boring-stuff-with-go/tree/main/ch7) after the Cheat Sheet

I. [String Manipulation cheatsheet](https://www.automatetheboringstuffwithgo.com/chapter-7/)
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 


## 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.