← Runnable Projects

阅读中文版本

Word Count CLI in Go

A Go program that counts words from a file or stdin, printing the total and the five most frequent words in descending order.

beginnergounverified

Project

go-word-count-cli

Entry point: main.go

Install

go version

Run

go run main.go words.txt

Test

go test ./...

Expected output

Prints the total word count, then the top five words with counts, sorted by frequency then alphabetically.

Dependencies

  • go >=1.21

What it does

Reads the input, splits it into words on non-letter boundaries, lowercases them, and tallies counts in a map. It then sorts by count descending and by word ascending for stable output, printing the top five.

package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
	"strings"
	"unicode"
)

func words(r *bufio.Scanner) map[string]int {
	counts := map[string]int{}
	for r.Scan() {
		for _, w := range strings.FieldsFunc(r.Text(), func(r rune) bool { return !unicode.IsLetter(r) }) {
			counts[strings.ToLower(w)]++
		}
	}
	return counts
}

func main() {
	var in *os.File
	if len(os.Args) > 1 {
		f, err := os.Open(os.Args[1])
		if err != nil {
			fmt.Fprintln(os.Stderr, "cannot open file:", err)
			os.Exit(1)
		}
		defer f.Close()
		in = f
	} else {
		in = os.Stdin
	}

	scanner := bufio.NewScanner(in)
	counts := words(scanner)
	total := 0
	for _, n := range counts {
		total += n
	}
	keys := make([]string, 0, len(counts))
	for k := range counts {
		keys = append(keys, k)
	}
	sort.Slice(keys, func(i, j int) bool {
		if counts[keys[i]] != counts[keys[j]] {
			return counts[keys[i]] > counts[keys[j]]
		}
		return keys[i] < keys[j]
	})

	fmt.Printf("total words: %d\n", total)
	fmt.Println("top words:")
	for i, k := range keys {
		if i == 5 {
			break
		}
		fmt.Printf("  %s: %d\n", k, counts[k])
	}
}

Run and verify

go run main.go words.txt

Run the test command to check the implementation: go test ./....

Expected Output

For a file where the word go appears three times and others fewer, the report begins with the total:

total words: 42
top words:
  go: 3
  the: 2
  a: 2
  run: 1
  test: 1

Limitations

Splits on non-letter runes, so it treats digits and apostrophes as separators and lowercases everything, which merges tokens that differ only by case.