I implemented the freq(1) program from Plan 9 in go.
/* print histogram of character frequencies */
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"os"
"sort"
"unicode"
)
var dflag = flag.Bool("d", false, "print decimal value")
var xflag = flag.Bool("x", false, "print hex value")
var oflag = flag.Bool("o", false, "print octal value")
var cflag = flag.Bool("c", false, "print unicode character if printable")
var rflag = flag.Bool("r", false, "count UTF8 sequences (runes)")
func main() {
var freq func(*os.File, map[int32]int) = freqBytes
counts := make(map[int32]int)
var codes []int32
flag.Parse()
if (!*dflag && !*oflag && !*xflag && !*cflag) {
*dflag = true
*oflag = true
*xflag = true
*cflag = true
}
if (*rflag) {
freq = freqRunes
}
if (len(flag.Args()) == 0) {
freq(os.Stdin, counts)
} else {
for _, arg := range flag.Args() {
if arg == "-" {
freq(os.Stdin, counts)
} else {
f, err := os.Open(arg)
if err != nil {
log.Fatal(err)
}
freq(f, counts)
f.Close()
}
}
}
for code := range counts {
codes = append(codes, code)
}
sort.Slice(codes, func(i, j int) bool { return codes[i] < codes[j] })
for _, code := range codes {
if (*dflag) {
fmt.Printf("%3d ", code)
}
if (*oflag) {
fmt.Printf("%03o ", code)
}
if (*xflag) {
fmt.Printf("%02x ", code)
}
if (*cflag) {
c := rune(code)
if (!unicode.IsPrint(c) || unicode.IsSpace(c)) {
c = '-'
}
fmt.Printf("%c ", c)
}
fmt.Printf("%8d\n", counts[code])
}
}
func freqRunes(f *os.File, counts map[int32]int) {
in := bufio.NewReader(f)
for {
r, n, err := in.ReadRune()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
if r == unicode.ReplacementChar && n == 1 {
continue
}
counts[r]++
}
}
func freqBytes(f *os.File, counts map[int32]int) {
in := bufio.NewReader(f)
for {
b, err := in.ReadByte()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
counts[int32(b)]++
}
}
freq(1) counts the occurrences of each byte (or each rune, if the -r
option is given) in the standard input or the given files. More information on the manual. Unprintable and space characters are replaced with a -
. Here is an example of usage with an Esperanto Lorem Ipsum:
$ echo "eĥoŝanĝo ĉiuĵaŭde" | ./freq -r
10 012 0a - 1
32 040 20 - 1
97 141 61 a 2
100 144 64 d 1
101 145 65 e 2
105 151 69 i 1
110 156 6e n 1
111 157 6f o 2
117 165 75 u 1
265 411 109 ĉ 1
285 435 11d ĝ 1
293 445 125 ĥ 1
309 465 135 ĵ 1
349 535 15d ŝ 1
365 555 16d ŭ 1
Each line prints the codepoint (in decimal, octal and hex) of the rune and the rune itself followed by the occurrence count.
How can I make the code more elegant and idiomatic?
:=
initialization shortcut instead ofvar name typeName = value
, you can usename := value
and go will infer the types for you. \$\endgroup\$