Go 语言完全指南 / 18 - 字符串:strings 包、strconv、unicode、正则
18 - 字符串
18.1 字符串基础
Go 的字符串是不可变的 UTF-8 字节序列。
package main
import (
"fmt"
"unsafe"
)
func main() {
s := "Hello, 世界"
// 字节长度
fmt.Println("字节数:", len(s)) // 13
// 字符数(rune 数量)
fmt.Println("字符数:", len([]rune(s))) // 9
// 字符串底层是 struct { pointer; length }
fmt.Println("大小:", unsafe.Sizeof(s)) // 16(两个指针大小)
// 字符串不可变
// s[0] = 'h' // 编译错误
// 创建新字符串
s2 := "h" + s[1:]
fmt.Println(s2)
}
字符串 vs []byte vs []rune
func main() {
s := "Hello, 世界"
// string → []byte(UTF-8 编码)
bytes := []byte(s)
fmt.Println(bytes) // [72 101 108 108 111 44 32 228 184 150 231 149 140]
// string → []rune(Unicode 码点)
runes := []rune(s)
fmt.Println(runes) // [72 101 108 108 111 44 32 19990 30028]
// []byte → string
s2 := string(bytes)
// []rune → string
s3 := string(runes)
fmt.Println(s2, s3)
}
18.2 strings 包
查找
import "strings"
func main() {
s := "Hello, World! Hello, Go!"
fmt.Println(strings.Contains(s, "Hello")) // true
fmt.Println(strings.Count(s, "Hello")) // 2
fmt.Println(strings.HasPrefix(s, "Hello")) // true
fmt.Println(strings.HasSuffix(s, "Go!")) // true
fmt.Println(strings.Index(s, "World")) // 7
fmt.Println(strings.LastIndex(s, "Hello")) // 14
fmt.Println(strings.ContainsAny(s, "xyz")) // false
fmt.Println(strings.ContainsRune(s, '世')) // false
}
转换
func main() {
s := "Hello, World!"
fmt.Println(strings.ToUpper(s)) // HELLO, WORLD!
fmt.Println(strings.ToLower(s)) // hello, world!
fmt.Println(strings.Title("hello world")) // Hello World
fmt.Println(strings.ToTitle(s)) // HELLO, WORLD!
// 修剪
s2 := " Hello, World! "
fmt.Println(strings.TrimSpace(s2)) // "Hello, World!"
fmt.Println(strings.Trim(s2, " ")) // "Hello, World!"
fmt.Println(strings.TrimLeft(s2, " ")) // "Hello, World! "
fmt.Println(strings.TrimRight(s2, " ")) // " Hello, World!"
fmt.Println(strings.TrimPrefix("##Hello", "##")) // "Hello"
fmt.Println(strings.TrimSuffix("Hello##", "##")) // "Hello"
}
分割与连接
func main() {
// 分割
s := "a,b,c,d,e"
fmt.Println(strings.Split(s, ",")) // [a b c d e]
fmt.Println(strings.SplitN(s, ",", 3)) // [a b c,d,e]
fmt.Println(strings.SplitAfter(s, ",")) // [a, b, c, d, e]
// 按空白分割
s2 := " hello world go "
fmt.Println(strings.Fields(s2)) // [hello world go]
// 连接
words := []string{"Go", "is", "awesome"}
fmt.Println(strings.Join(words, " ")) // Go is awesome
fmt.Println(strings.Join(words, "-")) // Go-is-awesome
// 重复
fmt.Println(strings.Repeat("Go!", 3)) // Go!Go!Go!
// 替换
s3 := "Hello World World"
fmt.Println(strings.Replace(s3, "World", "Go", 1)) // Hello Go World
fmt.Println(strings.ReplaceAll(s3, "World", "Go")) // Hello Go Go
}
Builder(高效拼接)
import (
"strings"
"testing"
)
// ❌ 效率低(每次 + 创建新字符串)
func concatBad(parts []string) string {
result := ""
for _, p := range parts {
result += p
}
return result
}
// ✅ 高效:使用 strings.Builder
func concatGood(parts []string) string {
var b strings.Builder
for _, p := range parts {
b.WriteString(p)
}
return b.String()
}
// 预分配容量
func concatBest(parts []string) string {
totalLen := 0
for _, p := range parts {
totalLen += len(p)
}
var b strings.Builder
b.Grow(totalLen) // 预分配
for _, p := range parts {
b.WriteString(p)
}
return b.String()
}
18.3 strconv 包
import "strconv"
func main() {
// 字符串 → 整数
n1, _ := strconv.Atoi("12345")
n2, _ := strconv.ParseInt("12345", 10, 64) // 十进制,64 位
n3, _ := strconv.ParseInt("ff", 16, 64) // 十六进制
fmt.Println(n1, n2, n3) // 12345 12345 255
// 整数 → 字符串
s1 := strconv.Itoa(12345)
s2 := strconv.FormatInt(12345, 10)
s3 := strconv.FormatInt(255, 16)
fmt.Println(s1, s2, s3) // 12345 12345 ff
// 字符串 → 浮点数
f1, _ := strconv.ParseFloat("3.14", 64)
fmt.Println(f1)
// 浮点数 → 字符串
fmt.Println(strconv.FormatFloat(3.14, 'f', 2, 64)) // 3.14
fmt.Println(strconv.FormatFloat(3.14, 'e', -1, 64)) // 3.14e+00
// 字符串 → 布尔
b1, _ := strconv.ParseBool("true")
b2, _ := strconv.ParseBool("1")
b3, _ := strconv.ParseBool("yes")
fmt.Println(b1, b2, b3) // true true false
// 布尔 → 字符串
fmt.Println(strconv.FormatBool(true)) // "true"
// 更快的转换(无错误处理)
s := strconv.Itoa(42)
n, _ := strconv.Atoi("42")
fmt.Println(s, n)
// 引用
fmt.Println(strconv.Quote("Hello\tWorld")) // "Hello\tWorld"
fmt.Println(strconv.QuoteRune('中')) // '中'
}
18.4 unicode 包
import (
"fmt"
"unicode"
)
func main() {
// 字符分类
fmt.Println(unicode.IsLetter('A')) // true
fmt.Println(unicode.IsDigit('5')) // true
fmt.Println(unicode.IsSpace(' ')) // true
fmt.Println(unicode.IsUpper('A')) // true
fmt.Println(unicode.IsLower('a')) // true
fmt.Println(unicode.IsPunct('!')) // true
fmt.Println(unicode.IsChinese('中')) // true
// 大小写转换
fmt.Printf("%c\n", unicode.ToUpper('a')) // A
fmt.Printf("%c\n", unicode.ToLower('A')) // a
fmt.Printf("%c\n", unicode.ToTitle('a')) // A
// 统计字符串中的中文字符数
s := "Hello, 世界! Go 语言"
chineseCount := 0
for _, r := range s {
if unicode.Is(unicode.Han, r) {
chineseCount++
}
}
fmt.Println("中文字符数:", chineseCount) // 4
// RuneReader 遍历
for _, r := range "Hello, 世界" {
fmt.Printf("%c (%U) ", r, r)
}
fmt.Println()
}
18.5 正则表达式
import (
"fmt"
"regexp"
)
func main() {
// 编译正则(推荐编译一次,多次使用)
re := regexp.MustCompile(`\d+`)
// 匹配
fmt.Println(re.MatchString("abc123")) // true
fmt.Println(re.MatchString("abc")) // false
// 查找
fmt.Println(re.FindString("abc123def456")) // 123
fmt.Println(re.FindAllString("a1b2c3", -1)) // [1 2 3]
fmt.Println(re.FindStringIndex("abc123")) // [3 6]
// 带捕获组
re2 := regexp.MustCompile(`(\w+)@(\w+)\.(\w+)`)
match := re2.FindStringSubmatch("[email protected]")
fmt.Println(match) // [[email protected] user example com]
// 命名捕获组
re3 := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})`)
match2 := re3.FindStringSubmatch("2024-01-15")
for i, name := range re3.SubexpNames() {
if i > 0 && name != "" {
fmt.Printf("%s: %s\n", name, match2[i])
}
}
// 替换
re4 := regexp.MustCompile(`\s+`)
fmt.Println(re4.ReplaceAllString("hello world go", " "))
// 替换(使用函数)
result := re4.ReplaceAllStringFunc("abc123def456", func(s string) string {
return "[" + s + "]"
})
fmt.Println(result)
// Split
re5 := regexp.MustCompile(`\d+`)
parts := re5.Split("abc123def456ghi", -1)
fmt.Println(parts) // [abc def ghi]
}
常用正则模式
var (
// 邮箱
EmailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`)
// 手机号(中国)
PhoneRegex = regexp.MustCompile(`^1[3-9]\d{9}$`)
// IP 地址
IPRegex = regexp.MustCompile(`^(\d{1,3}\.){3}\d{1,3}$`)
// URL
URLRegex = regexp.MustCompile(`^https?://[^\s]+$`)
// 中文
ChineseRegex = regexp.MustCompile(`[\p{Han}]+`)
)
func validateEmail(email string) bool {
return EmailRegex.MatchString(email)
}
18.6 strings.Cut(Go 1.18+)
func main() {
// 字符串分割的首选方式
before, after, found := strings.Cut("key=value", "=")
fmt.Println(before, after, found) // key value true
before, after, found = strings.Cut("no-equal", "=")
fmt.Println(before, after, found) // no-equal false
// 用于解析配置行
line := "DATABASE_URL=postgres://localhost:5432/mydb"
key, value, ok := strings.Cut(line, "=")
if ok {
fmt.Printf("%s = %s\n", key, value)
}
}
🏢 业务场景
- 数据清洗:strings.Trim/Replace 清洗用户输入
- URL 解析:strings.Split + strings.Cut 解析 URL 参数
- 日志分析:正则提取日志中的关键信息
- 模板渲染:strings.Builder 高效拼接 HTML
- 输入验证:正则表达式验证邮箱、手机号等