在Go语言的标准库支持下,构建一个统计文本行数和单词数的小工具并不复杂。我们可以利用bufio和strings包完成核心逻辑,既能处理小文件也能通过流式读取应对大文件。下面先给出一个基础实现,再讨论优化点。

基础实现方案
最直观的做法是用bufio.Scanner按行扫描文件,每读一行行数加一,然后用strings.Fields把行内文本按空白拆成单词切片,累加长度即可。这种方式代码量少,适合几 MB 以内的文本。
需要注意的是,strings.Fields会自动以空格、制表符、换行等空白字符切分,连续空白会被视为一个分隔符,因此能正确处理中英文混排里多余的空格。下面示例展示了一个完整的命令行工具,接收文件路径作为参数并输出统计结果。
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("用法: go run main.go 文件路径")
return
}
path := os.Args[1]
file, err := os.Open(path)
if err != nil {
fmt.Printf("打开文件失败: %vn", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
lineCount := 0
wordCount := 0
for scanner.Scan() {
line := scanner.Text()
lineCount++
words := strings.Fields(line)
wordCount += len(words)
}
if err := scanner.Err(); err != nil {
fmt.Printf("读取文件出错: %vn", err)
return
}
fmt.Printf("行数: %dn", lineCount)
fmt.Printf("单词数: %dn", wordCount)
}
上面的代码在扫描时直接调用scanner.Text()拿到字符串,没有额外缓存全部内容,因此内存占用较低。对于普通日志或代码文件,这种实现已经足够稳定。
不过strings.Fields对中文单词的切分并不符合自然语言习惯,它会把整行中文当成一个“单词”,因为中文之间没有空白。如果需求是统计英文单词,这样没问题;若要按中文分词,需要引入分词库。明确需求边界能减少后期返工。
大文件与特殊场景优化
当文件体积达到数百 MB 甚至 GB 时,bufio.Scanner的默认缓冲可能不够,会出现token too long的错误。此时可以通过scanner.Buffer方法手动扩大缓冲,或者改用bufio.Reader配合ReadString逐段读取,自己维护换行逻辑。
另一个常见问题是空行是否计入行数。上面的示例把空行也计入,因为scanner.Scan()遇到换行就会返回一次。如果业务要求忽略完全空白的行,只需在循环里加一句判断:若strings.TrimSpace(line) == ""则lineCount不加。这样的微调体现了文本解析工具应随场景定制。
// 忽略空行的统计片段
for scanner.Scan() {
line := scanner.Text()
if strings.TrimSpace(line) == "" {
continue
}
lineCount++
wordCount += len(strings.Fields(line))
}
如果还要支持统计字符数或特定模式的词频,可以在同一个循环里扩展逻辑,比如用unicode包判断字符类型,或用regexp查找匹配。Go的并发特性也允许你把文件分块后交给多个goroutine处理,最后汇总结果,但要注意行边界不能切在半行上。
总的来说,用Golang写文本解析工具的核心在于选对读取方式、明确单词定义、处理好边界情况。基础版几十行就能跑起来,需要应对生产环境时再逐步加入缓冲控制、并发和更细的统计维度即可。
小结
通过bufio.Scanner与strings.Fields的组合,我们快速实现了行数和单词数的统计。该工具结构清晰,易于根据空行忽略、中文分词、大文件缓冲等需求做扩展。掌握这种小工具的写法,能帮你应对日常脚本化和自动化处理文本的任务。