strings.Fields 不够用,因它仅按空白符切分、不处理标点,导致“hello,”与“hello”被计为不同单词;正确做法是先用正则[^\p{L}\p{N}]+将非字母数字字符替换为空格,再调用strings.Fields。

直接用 map[string]int 统计即可,但必须先清洗文本、正确分词,否则结果完全不可信。
为什么 strings.Fields 不够用?
它只按空白符(空格、制表符、换行)切分,对标点符号毫无处理能力。比如 "hello," 和 "hello" 会被当作两个不同单词;"Go!" 和 "Go" 也会被重复计数。
- 常见错误现象:
map中出现"world."、"test?"、"Go!"等带标点的键 - 推荐做法:用
regexp.MustCompile(`[^p{L}p{N}]+`)替换所有非字母数字字符为空格(支持 Unicode),再strings.Fields - 性能影响:正则预编译一次即可,避免在循环里反复调用
regexp.Compile
WordCount 函数实现时最常踩的坑
Go 语言之旅(Tour of Go)的 WordCount 练习题是典型陷阱区——很多人误把 utf8.RuneCountInString(word) 当成频次统计,实际那是算字符长度。
- 错误写法:
c[v] = utf8.RuneCountInString(v)→ 把"the"记为3,不是出现次数 - 正确写法:
c[v]++或c[v] = c[v] + 1,依赖 map 默认零值行为 - 大小写敏感问题:不加
strings.ToLower会导致"The"和"the"被分开计数 - 空字符串过滤:
strings.Fields后仍可能有空串,需加if len(word) == 0 { continue }
大文件读取时如何避免内存爆炸?
别用 ioutil.ReadFile 或 os.ReadFile 一次性加载全文——几 MB 的日志就可能吃光百 MB 内存。应该流式处理。
- 用
bufio.Scanner逐行读,每行处理完立即丢弃 - 注意
Scanner默认缓冲区上限是 64KB,超长行会报scanner: token too long,可调用scanner.Buffer(make([]byte, 64*1024), 1 扩容 - 若需按单词而非按行处理,改用
bufio.NewReader+ReadString(' ')+ 自定义分词逻辑更可控 - 不要在循环内反复
make(map[string]int),复用同一个map实例
真正难的不是“怎么统计”,而是“怎么定义一个‘词’”——英文靠空格+标点还勉强,中文、日文、阿拉伯文必须引入分词库;而哪怕英文,像 don't、co-op、U.S.A. 也得按业务规则切。别跳过清洗环节,那才是频次准确的前提。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











