Go 从 0 到精通 · 第 21 课:字符串处理与常用工具库
Go 从 0 到精通 · 第 21 课:字符串处理与常用工具库
学习定位:这是整套 Go 教程的第 21 课。
前置要求:已经完成第 20 课,掌握了时间处理。需要熟悉第 11 课的字符串、byte与rune基础。
本课目标:掌握strings、strconv、unicode三个包的常用函数,能高效完成文本清洗、拼接、拆分、查找、替换和类型转换任务。
1. 本课你要解决的核心问题
在第 11 课你已经理解了字符串的底层是 []byte,中文用 rune 处理。但在实际开发中,你面对的往往不是"遍历字符串",而是:
- 用户输入的文本里有前后空格,要去掉
- 从配置文件读到的字符串要拆分成列表
- 要判断一个字符串是否包含某个子串
- 要把数字转成字符串、字符串转回数字
- 要判断一个字符是中文、数字还是字母
这些操作如果自己用 for 循环写,既繁琐又容易出错。Go 的标准库已经帮你做好了。
你需要搞明白以下问题:
strings包有哪些常用函数,怎么用strconv包怎么在字符串和数字之间转换unicode包怎么判断字符类型- 这些函数组合起来怎么处理真实场景
学完这一课,你就能高效处理各种文本数据了。
2. strings 包——字符串操作瑞士军刀
strings 包是 Go 中最常用的字符串工具库。它的函数非常多,我们按功能分类逐个讲解。
2.1 大小写转换
1 | package main |
Title vs ToTitle 的区别(很多人搞混):
strings.Title("hello world")→"Hello World"(每个单词首字母大写)strings.ToTitle("hello world")→"HELLO WORLD"(每个字符转为 Unicode title case)
注意:Go 1.18 之后
strings.Title已被标记为 deprecated,推荐用golang.org/x/text/cases包。但在学习阶段了解即可。
2.2 去除空白
1 | s := " Hello, Go! " |
Trim 去除指定字符
1 | s := "###Hello###" |
实战:清理用户输入
1 | func cleanInput(input string) string { |
2.3 查找
判断是否包含
1 | s := "Hello, Go World!" |
判断开头和结尾
1 | s := "config.yaml" |
实战:根据文件扩展名做不同处理
1 | func processFile(filename string) { |
查找位置
1 | s := "hello world hello go" |
2.4 替换
1 | s := "Hello, Go! Go is great. Go!" |
实战:模板替换
1 | func renderTemplate(template string, data map[string]string) string { |
2.5 拆分
Split:按分隔符拆分
1 | s := "apple,banana,cherry" |
SplitN:限制拆分数量
1 | s := "user:password:host:port" |
Fields:按空白拆分(连续空白算一个)
1 | s := " hello world go " |
实战:解析命令行参数
1 | input := " git commit -m \"fix bug\" " |
SplitAfter:保留分隔符
1 | s := "a,b,c" |
2.6 拼接
Join:最高效的拼接方式
1 | parts := []string{"apple", "banana", "cherry"} |
Join 是拼接字符串列表的首选方式,比循环 += 高效得多(因为 += 每次都分配新内存)。
各种拼接方式的性能对比
1 | // 方式一:+= 拼接(最慢,每次分配新内存) |
strings.Builder:高效拼接器
1 | package main |
2.7 重复
1 | fmt.Println(strings.Repeat("=-", 10)) |
2.8 strings 包速查表
| 函数 | 用途 | 示例 |
|---|---|---|
Contains(s, substr) |
是否包含 | Contains("hello", "ell") → true |
HasPrefix(s, prefix) |
是否以此开头 | HasPrefix("hello", "hel") → true |
HasSuffix(s, suffix) |
是否以此结尾 | HasSuffix("file.go", ".go") → true |
Index(s, substr) |
第一次出现的位置 | Index("abcabc", "bc") → 1 |
Count(s, substr) |
出现次数 | Count("aaa", "aa") → 1 |
ReplaceAll(s, old, new) |
全部替换 | ReplaceAll("aaa", "a", "b") → "bbb" |
Split(s, sep) |
按分隔符拆分 | Split("a,b", ",") → ["a","b"] |
Fields(s) |
按空白拆分 | Fields(" a b ") → ["a","b"] |
Join(slice, sep) |
拼接 | Join(["a","b"], "-") → "a-b" |
Trim(s, cutset) |
去除首尾指定字符 | Trim("##hi#", "#") → "hi" |
TrimSpace(s) |
去除首尾空白 | TrimSpace(" hi ") → "hi" |
ToUpper(s) |
转大写 | ToUpper("hi") → "HI" |
ToLower(s) |
转小写 | ToLower("HI") → "hi" |
Repeat(s, count) |
重复 | Repeat("Go", 3) → "GoGoGo" |
3. strconv 包——字符串与数字互转
3.1 整数转字符串
1 | package main |
3.2 浮点数转字符串
1 | // FormatFloat:格式 + 精度 + 位宽 |
3.3 布尔值转字符串
1 | fmt.Println(strconv.FormatBool(true)) // "true" |
3.4 字符串转整数
1 | // Atoi:ASCII to int |
3.5 字符串转浮点数
1 | f, err := strconv.ParseFloat("3.14159", 64) |
3.6 字符串转布尔值
1 | // 接受 "1", "t", "T", "true", "TRUE", "True" |
3.7 错误处理
strconv 的解析函数在转换失败时会返回一个特殊的错误类型 *strconv.NumError:
1 | n, err := strconv.Atoi("abc") |
3.8 strconv 包速查表
| 函数 | 方向 | 示例 |
|---|---|---|
Itoa(n) |
int → string | Itoa(42) → "42" |
Atoi(s) |
string → int | Atoi("42") → 42, nil |
FormatInt(n, base) |
int64 → string | FormatInt(255, 16) → "ff" |
ParseInt(s, base, bits) |
string → int64 | ParseInt("ff", 16, 64) → 255 |
FormatFloat(f, fmt, prec, bits) |
float64 → string | FormatFloat(3.14, 'f', 2, 64) → "3.14" |
ParseFloat(s, bits) |
string → float64 | ParseFloat("3.14", 64) → 3.14 |
FormatBool(b) |
bool → string | FormatBool(true) → "true" |
ParseBool(s) |
string → bool | ParseBool("true") → true |
Quote(s) |
string → 带引号的字符串 | Quote("hi") → "\"hi\"" |
Unquote(s) |
带引号的字符串 → string | Unquote("\"hi\"") → "hi" |
4. unicode 包——字符类型判断
4.1 判断字符类型
1 | package main |
输出示例:
1 | 字符 'A': |
4.2 字符大小写转换
1 | ch := 'a' |
4.3 实战:统计文本中各类字符的数量
1 | package main |
5. 实战综合示例
5.1 用户输入清洗与验证
1 | package main |
输出:
1 | 输入: [ Alice ] → 清洗后: [alice] |
5.2 CSV 行解析器
1 | package main |
输出:
1 | 表头: [name age city] |
5.3 文本统计工具
1 | package main |
6. fmt 包中与字符串相关的技巧
6.1 fmt.Sprintf:格式化字符串
1 | name := "Alice" |
常用格式化动词:
| 动词 | 含义 | 示例 |
|---|---|---|
%s |
字符串 | "hello" |
%d |
十进制整数 | 42 |
%f |
浮点数 | 3.14159 |
%.2f |
保留2位小数 | 3.14 |
%t |
布尔值 | true |
%v |
默认格式 | 任意值 |
%T |
类型 | int |
%x |
十六进制 | ff |
%% |
百分号 | % |
6.2 fmt.Errorf:格式化错误信息
1 | err := fmt.Errorf("文件 %s 不存在(大小: %d)", "data.txt", 0) |
6.3 fmt.Sprint 系列
1 | // Sprint:连接多个参数为字符串(无分隔符) |
7. 常见坑总结
7.1 strings.Split 在空字符串上的行为
1 | // 空字符串按逗号拆分,得到一个包含空串的切片,不是空切片! |
7.2 strings.Replace 的 n 参数
1 | s := "aaa" |
7.3 strconv.Atoi 和中文数字
1 | // Atoi 只处理 ASCII 数字 |
7.4 strconv.ParseFloat 的精度问题
1 | // 浮点数的精度是有限的 |
7.5 strings.ToLower / ToUpper 和 Unicode
1 | // 对于纯 ASCII 没问题 |
7.6 unicode.IsDigit vs unicode.IsNumber
1 | // IsDigit 只匹配 0-9 |
7.7 字符串拼接用 += 而不是 strings.Join
1 | // 错误:在循环中用 += 拼接,性能极差 |
7.8 strconv.Itoa 和负数
1 | // Itoa 可以处理负数 |
8. 本课练习
练习 1:字符串清洗
要求:
- 写一个函数,接收用户输入的字符串
- 去掉前后空白、转为小写
- 去掉所有标点符号(提示:
strings.Map+unicode.IsPunct) - 返回清洗后的结果
练习 2:简易计算器
要求:
- 接收一个字符串表达式,如
"123 + 456" - 拆分出两个数字和运算符(提示:
strings.SplitN+strings.Fields) - 用
strconv.Atoi转成数字 - 计算结果并输出
练习 3:统计文章字数
要求:
- 写一个函数统计中文文章的字数
- 中文按字符计数,英文按单词计数
- 统计有多少个段落(以空行分隔)
练习 4:配置文件解析
要求:
- 解析
key=value格式的配置行 - 跳过注释行(以
#开头) - 跳过空行
- 去掉值中的前后空白
- 返回
map[string]string
练习 5:字符串加密(简单版)
要求:
- 写一个凯撒加密函数:每个字母移动 n 位(如
a→d,b→e) - 用
unicode.IsLetter判断是否是字母 - 用
unicode.IsUpper判断大小写 - 非字母字符保持不变
9. 自测题
9.1 概念题
strings.Split和strings.Fields有什么区别?strings.Join为什么比循环+=拼接高效?strconv.Itoa和strconv.FormatInt(42, 10)有什么关系?strconv.Atoi能处理中文数字"四十二"吗?unicode.IsDigit和unicode.IsNumber的区别是什么?strings.Replace的第三个参数0和-1分别是什么意思?strings.Builder的Reset方法有什么用?strconv.ParseInt的base参数设为0是什么意思?strings.TrimPrefix和strings.Trim有什么区别?- 用什么函数可以把字符串中的每个字符按自定义规则映射?
9.2 代码阅读题
预测以下代码的输出:
1 | func mystery(s string) string { |
点击查看答案
1 | Hello World Go |
解释:
strings.Fields去掉多余空白,拆成["hello", "WORLD", "go"]- 每个单词首字母大写 + 其余小写
- 用空格
Join回去
10. 本课总结
这一课你学到了字符串处理的三个核心包。
| 场景 | 推荐函数 |
|---|---|
| 去掉空白 | strings.TrimSpace |
| 大小写转换 | strings.ToUpper / ToLower |
| 查找子串 | strings.Contains / Index / Count |
| 判断开头/结尾 | strings.HasPrefix / HasSuffix |
| 替换 | strings.ReplaceAll |
| 拆分 | strings.Split(有分隔符)/ strings.Fields(按空白) |
| 拼接 | strings.Join(切片)/ strings.Builder(复杂拼接) |
| 整数 ↔ 字符串 | strconv.Itoa / strconv.Atoi |
| 浮点数 ↔ 字符串 | strconv.FormatFloat / strconv.ParseFloat |
| 布尔 ↔ 字符串 | strconv.FormatBool / strconv.ParseBool |
| 判断字符类型 | unicode.IsLetter / IsDigit / IsSpace / IsUpper |
最重要的三件事:
- 字符串拼接用
Join或Builder,不要在循环里用+= - 字符串转数字用
strconv.Atoi/ParseFloat,一定要检查错误 - 判断字符类型用
unicode包,不要自己写范围判断
11. 下一课预告
下一课我们学习 JSON 编解码。
会重点讲:
encoding/json包的使用- 结构体与 JSON 的相互转换
- 结构体标签(
json:"field_name") - 处理未知结构的 JSON
学完下一课,你就能处理接口响应、配置文件和数据交换了。
评论





