DEV Community

Toul
Toul

Posted on • Originally published at automatetheboringstuffwithgo.com

Ch. 7 String Manipulation

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

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.")
    }
}

Enter fullscreen mode Exit fullscreen mode

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)
    }
}

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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]
}

Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)