Video Coming Soon...
34: tr
The tr command is another very useful tool that "translates" one set of characters to another set. It reads the input, looks at each character, and changes any that match the input set to the output set.
Most of the documentation for tr talks about "array1 and array1" which are just the two sets of characters you give tr as command line options.
The Challenge
You are required to implement the following options to tr:
-c-- Compliment ofarray1, which means inverse, so every character not inarray1.-d-- Delete chars that matcharray1.-s-- Squeeze the characters found inarray2, which means to remove duplicates.-t-- Truncatearray1toarray2length.
You can find the documentation for tr here:
Requirements
I used all of the basic packages you'll need for most of these tools like bufio and os, but also strings this time.
See the list of requirements
- bufio -- https://pkg.go.dev/bufio
- flag -- https://pkg.go.dev/flag
- fmt -- https://pkg.go.dev/fmt
- log -- https://pkg.go.dev/log
- os -- https://pkg.go.dev/os
- strings -- https://pkg.go.dev/strings
Spoilers
As usual, my implementation is only a few of the options and just enough to get you started if you're stuck.
See my first version code
View Source file go-coreutils/tr/main.go Onlypackage main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"strings"
)
type Opts struct {
Compliment bool
Delete bool
Squeeze bool
Truncate bool
}
type Translator struct {
opts Opts
TwoArgs bool
RuneMap map[rune]rune
}
func (tr *Translator) ParseOpts() {
var from []rune
var to []rune
tr.RuneMap = make(map[rune]rune)
tr.TwoArgs = true
flag.BoolVar(&tr.opts.Compliment, "c", false, "Use compliment of array1")
flag.BoolVar(&tr.opts.Delete, "d", false, "Delete chars in array1")
flag.BoolVar(&tr.opts.Squeeze, "s", false, "Squeeze chars in array2")
flag.BoolVar(&tr.opts.Truncate, "t", false, "Truncate array1 to array2 length")
flag.Parse()
args := flag.Args()
if flag.NArg() == 1 {
from, to = []rune(args[0]), []rune(args[0])
tr.TwoArgs = false
} else if flag.NArg() == 2 {
from, to = []rune(args[0]), []rune(args[1])
} else {
log.Fatal("USAGE: tr [-c] [-d] [-s] [-t] <array1> [array2]")
}
for i, ch := range from {
tr.RuneMap[ch] = to[i]
}
}
func (tr *Translator) Translate(from rune) rune {
if !tr.TwoArgs { log.Fatal("translate requires two args") }
to, ok := tr.RuneMap[from]
if ok {
return to
} else {
return from
}
}
func (tr *Translator) Delete(line string) string {
result := make([]rune, 0, len(line))
for _, ch := range []rune(line) {
_, ok := tr.RuneMap[ch]
if !ok {
result = append(result, ch)
}
}
return string(result)
}
func (tr *Translator) Process(line string) {
if tr.opts.Delete {
line = tr.Delete(line)
} else {
line = strings.Map(tr.Translate, line)
}
fmt.Print(line)
}
func main() {
tr := new(Translator)
tr.ParseOpts()
scan := bufio.NewScanner(os.Stdin)
for scan.Scan() {
line := scan.Text() + "\n"
tr.Process(line)
}
}
Testing It
If you want to push your learning further then you can try to implement an automated test for this. I actually need to learn how to test utilities like this with Go, so for now just consider this an extra challenge for later until I learn how to teach it.
Register for Learn Go the Hard Way
Register today for the course and get the all currently available videos and lessons, plus all future modules for no extra charge.