bufio

Package bufio implements buffered I/O. It wraps an io.Reader or io.Writer object, creating another object (Reader or Writer) that also implements the interface but provides buffering and some help for textual I/O.

bufio.NewReader

func main() {
	counts := make(map[rune]int)    // 1. 统计不同 rune 的个数
	var utflen [utf8.UTFMax + 1]int // 2. 统计占用不同字节数(1-4)的字符的个数(utf8.UTFMax == 4; utflen[0] is not used)
	invalid := 0                    // 3. 统计不合法的 UTF-8 字符个数
	in := bufio.NewReader(os.Stdin)
	for {
		r, n, err := in.ReadRune() // rune, size in byte, error
		if err == io.EOF {         // the only error expected is end-of-file
			break
		}
		if err != nil {
			fmt.Fprintf(os.Stderr, "charcount: %v\n", err)
			os.Exit(1)
		}
        // If the input was not a legal UTF-8 encoding of a rune, 
        // the returned rune is unicode.ReplacementChar and the length is 1.
		if r == unicode.ReplacementChar && n == 1 {
			invalid++
			continue
		}
		counts[r]++
		utflen[n]++
	}
	fmt.Printf("rune\tcount\n")
	for c, n := range counts {
		fmt.Printf("%q\t%d\n", c, n)
	}
	fmt.Print("\nlen\tcount\n")
	for i, n := range utflen {
		if i > 0 {
			fmt.Printf("%d\t%d\n", i, n)
		}
	}
	if invalid > 0 {
		fmt.Printf("\n%d invalid UTF-8 characters\n", invalid)
	}
}
// go run main.go < main.go

bufio.NewScanner

func main() {   // implementation of string set in Go
    seen := make(map[string]bool)
    // Returns a new Scanner to read from r (of type io.Reader). 
    // The split function defaults to ScanLines. (ScanBytes, ScanRunes, ScanWords)
	input := bufio.NewScanner(os.Stdin) 
	for input.Scan() {
		line := input.Text()
		if !seen[line] {
			seen[line] = true
			fmt.Println(line)
		}
	}
	if err := input.Err(); err != nil { // returns the first non-EOF error that was encountered by the Scanner.
		fmt.Fprintf(os.Stderr, "dedup: %v\n", err)
		os.Exit(1)
	}
}