
本文介绍如何用 go 语言读取远程文本文件、解析单词列表,并通过切片与映射(map)两种方式统计两文件间的重复单词数量,重点解决数组/切片类型混淆、循环变量误用等常见编译错误。
本文介绍如何用 go 语言读取远程文本文件、解析单词列表,并通过切片与映射(map)两种方式统计两文件间的重复单词数量,重点解决数组/切片类型混淆、循环变量误用等常见编译错误。
在 Go 中处理字符串集合交集时,初学者常因混淆数组([N]string)与切片([]string)而遭遇编译失败——正如原代码中 bufferUrl1 是固定长度数组,却直接传入期望 string 和 []string 的 stringInSlice 函数,导致类型不匹配错误:
.\hello.go:69: cannot use bufferUrl1 (type [40000]string) as type string in argument to stringInSlice .\hello.go:69: cannot use buffer (type [2000]string) as type []string in argument to stringInSlice
核心问题有两个:
- 循环变量误用:for v := range bufferUrl1 中 v 是索引(int),而非元素值;应改用 for _, s := range bufferUrl1 获取每个字符串 s;
- 类型不兼容:Go 中 [N]string 是值类型数组,[]string 是引用类型切片,二者不可互换。stringInSlice 声明为 func(string, []string) bool,因此必须传入切片。
✅ 正确做法是使用 make 动态创建切片,并在扫描时动态追加(更安全,避免越界):
package main
import (
"fmt"
"log"
"net/http"
"bufio"
"strings"
)
func stringInSlice(str string, list []string) bool {
for _, v := range list {
if v == str {
return true
}
}
return false
}
func main() {
listURL := "https://gist.githubusercontent.com/alexcesaro/c9c47c638252e21bd82c/raw/bd031237a56ae6691145b4df5617c385dffe930d/list.txt"
file1URL := "https://gist.githubusercontent.com/alexcesaro/4ebfa5a9548d053dddb2/raw/abb8525774b63f342e5173d1af89e47a7a39cd2d/file1.txt"
// ✅ 使用 make 创建可增长切片(推荐:无需预估容量,用 append 安全扩容)
var wordList, file1Words []string
// 读取 list.txt
resp1, err := http.Get(listURL)
if err != nil {
log.Fatalf("获取词表失败: %v", err)
}
defer resp1.Body.Close()
scanner1 := bufio.NewScanner(resp1.Body)
for scanner1.Scan() {
line := strings.TrimSpace(scanner1.Text())
if line != "" {
wordList = append(wordList, line)
}
}
// 读取 file1.txt
resp2, err := http.Get(file1URL)
if err != nil {
log.Fatalf("获取文件1失败: %v", err)
}
defer resp2.Body.Close()
scanner2 := bufio.NewScanner(resp2.Body)
for scanner2.Scan() {
line := strings.TrimSpace(scanner2.Text())
if line != "" {
file1Words = append(file1Words, line)
}
}
// ✅ 修正循环:遍历 file1Words 中每个字符串 s,检查是否在 wordList 中
sibling := 0
for _, s := range file1Words {
if stringInSlice(s, wordList) {
sibling++
fmt.Printf("FIND: %q\n", s)
}
}
fmt.Printf("共找到 %d 个共有单词\n", sibling)
}
⚠️ 注意事项:
- 预分配数组(如 [2000]string)易引发越界 panic(若实际行数超限),强烈建议始终使用 []string + append;
- stringInSlice 时间复杂度为 O(n),当词表较大时(如数万单词),性能会急剧下降;
✅ 进阶优化:用 map 实现 O(1) 查找
将 wordList 转为 map[string]struct{}(空结构体最省内存),大幅提升匹配效率:
// 替换原 wordList 切片构建逻辑:
wordSet := make(map[string]struct{})
for _, word := range wordList {
wordSet[word] = struct{}{}
}
// 替换匹配逻辑:
sibling := 0
for _, s := range file1Words {
if _, exists := wordSet[s]; exists {
sibling++
fmt.Printf("FIND: %q\n", s)
}
}
fmt.Printf("共找到 %d 个共有单词(哈希查找)\n", sibling)
总结:Go 的类型系统严格但富有表现力。掌握切片与数组的本质区别、善用 range 语义、并根据数据规模选择合适的数据结构(切片 for small sets, map for large lookups),是写出健壮 Go 代码的关键。











