GoClub Pro

Go-string

最近更新:2026-10-24
本页的笔记正文在原笔记的附件文档里,已全部转成网页全文放在下面,在线就能看;原始笔记文件见「Go语言相关笔记」页的原始链接。

附件文档全文(Go strings )#

Go `strings`包功能详解与使用指南

你想要详细了解Go语言中strings包(注意是复数形式strings,而非string)的功能和使用方法,我会从基础到进阶,全面讲解这个包的核心功能和常用函数。

Go语言的strings包是处理字符串的核心标准库,提供了大量实用的字符串操作函数,比如查找、替换、分割、拼接、大小写转换等,无需自己重复实现这些基础功能。

一、核心功能分类与常用函数详解

1. 字符串长度与判断类

这类函数主要用于判断字符串的属性(是否为空、是否包含特定前缀/后缀、是否包含子串等)。

函数

功能

示例

len(s string) int

(内置函数)返回字符串字节数(非字符数,中文占3字节)

len("Go语言") // 输出 8

strings.Empty(s string) bool

判断字符串是否为空(长度为0)

strings.Empty("") // true

strings.HasPrefix(s, prefix string) bool

判断字符串是否以指定前缀开头

strings.HasPrefix("hello", "he") // true

strings.HasSuffix(s, suffix string) bool

判断字符串是否以指定后缀结尾

strings.HasSuffix("hello", "lo") // true

strings.Contains(s, substr string) bool

判断字符串是否包含指定子串

strings.Contains("hello", "ell") // true

strings.ContainsAny(s, chars string) bool

判断字符串是否包含chars中的任意字符

strings.ContainsAny("hello", "xyz") // false

strings.ContainsRune(s string, r rune) bool

判断字符串是否包含指定Unicode字符

strings.ContainsRune("Go语言", '语') // true

2. 字符串修改类

这类函数用于修改字符串(Go中字符串不可变,所有修改函数都会返回新字符串)。

Go
package main

import (
"fmt"
"strings"
)

func main() {
// 1. 大小写转换
s := "Hello Go 语言"
fmt.Println(strings.ToUpper(s)) // HELLO GO 语言
fmt.Println(strings.ToLower(s)) // hello go 语言
fmt.Println(strings.Title(s)) // Hello Go 语言(每个单词首字母大写)

// 2. 去除空白/指定字符
s2 := " hello world "
fmt.Println(strings.TrimSpace(s2)) // "hello world"(去除首尾空白)
fmt.Println(strings.Trim(s2, " hd")) // "ello worl"(去除首尾h、d、空格)
fmt.Println(strings.TrimLeft(s2, " ")) // "hello world "(仅去除左侧)
fmt.Println(strings.TrimRight(s2, " ")) // " hello world"(仅去除右侧)

// 3. 替换
s3 := "aaa-bbb-aaa"
// 替换所有匹配项
fmt.Println(strings.ReplaceAll(s3, "aaa", "ccc")) // ccc-bbb-ccc
// 替换指定次数(n=-1表示全部)
fmt.Println(strings.Replace(s3, "aaa", "ccc", 1)) // ccc-bbb-aaa
}

3. 字符串分割与拼接

这类函数用于将字符串拆分为切片,或将切片拼接为字符串。

Go
package main

import (
"fmt"
"strings"
)

func main() {
// 1. 分割
s := "a,b,c,d"
// 按指定分隔符分割
arr1 := strings.Split(s, ",")
fmt.Println(arr1) // [a b c d]

// 按换行符分割(处理多行文本)
s2 := "line1\nline2\nline3"
arr2 := strings.SplitLines(s2)
fmt.Println(arr2) // [line1 line2 line3]

// 按指定字符数分割
arr3 := strings.SplitN(s, ",", 2)
fmt.Println(arr3) // [a b,c,d](只分割1次,得到2个元素)

// 2. 拼接
arr := []string{"a", "b", "c"}
// 用指定分隔符拼接切片
fmt.Println(strings.Join(arr, "-")) // a-b-c

// 直接拼接多个字符串(无分隔符)
fmt.Println(strings.Join([]string{"hello", "world"}, "")) // helloworld
}

4. 字符串查找与索引

这类函数用于查找子串在字符串中的位置。

Go
package main

import (
"fmt"
"strings"
)

func main() {
s := "hello world"

// 查找子串首次出现的索引(从0开始),未找到返回-1
fmt.Println(strings.Index(s, "world")) // 6
fmt.Println(strings.Index(s, "xyz")) // -1

// 查找子串最后一次出现的索引
fmt.Println(strings.LastIndex(s, "l")) // 9

// 查找任意字符首次出现的索引
fmt.Println(strings.IndexAny(s, "xyz")) // -1
fmt.Println(strings.IndexAny(s, "ow")) // 4

// 查找指定字符首次出现的索引(rune支持中文)
fmt.Println(strings.IndexRune("Go语言", '语')) // 2
}

5. 字符串重复与比较

Go
package main

import (
"fmt"
"strings"
)

func main() {
// 重复字符串n次
fmt.Println(strings.Repeat("ab", 3)) // ababab

// 字符串比较(忽略大小写)
fmt.Println(strings.EqualFold("Hello", "hello")) // true

// 按字典序比较字符串(返回-1/0/1)
fmt.Println(strings.Compare("a", "b")) // -1
fmt.Println(strings.Compare("b", "a")) // 1
fmt.Println(strings.Compare("a", "a")) // 0
}

二、注意事项

  1. 字符串不可变:Go中字符串是不可变的字节序列,strings包的所有修改函数都会返回新字符串,原字符串不会被改变。
  2. 中文处理:len()返回的是字节数,而非字符数;处理中文需用rune类型(strings.IndexRune/strings.ContainsRune)。
  3. 空值处理:strings.Empty("") 返回true,但strings.Empty(" ") 返回false(空格不是空字符串),需用strings.TrimSpace先去空格再判断。

总结

  1. strings包是Go处理字符串的核心工具,覆盖了判断、修改、分割、拼接、查找等所有基础场景,优先使用标准库函数而非手写实现。
  2. 核心注意点:Go字符串不可变(修改返回新值)、len()统计字节数(非字符数)、中文处理需用rune相关函数。
  3. 高频函数:strings.Contains(包含判断)、strings.Split/Join(分割/拼接)、strings.TrimSpace(去空格)、strings.ReplaceAll(替换)。

|(注:文档部分内容可能由 AI 生成)

附件原文:onenote-go-string-Go_strings_.docx(点击下载)

附件文档全文( Go strings )#

深入探究 Go `strings` 包底层实现

你现在想深入了解Go语言strings包中核心函数的底层实现原理,这能帮助你更透彻地理解这些函数的执行逻辑和性能特点。

strings包的底层实现完全基于Go的基础数据结构([]byte、rune)和基础算法,没有依赖额外的复杂库。核心思路是:将字符串转换为字节切片([]byte)进行底层操作,因为字符串在Go中本质是只读的字节序列,直接操作字节切片效率更高。

一、核心底层基础

在看具体函数实现前,先明确两个底层基础:

  1. 字符串的本质:string在Go的底层结构是一个包含指针(指向字节数组)和长度的结构体,源码定义如下:

Go
type stringStruct struct {
str unsafe.Pointer // 指向字节数组的指针
len int // 字节长度
}

因此,字符串的所有操作本质都是对这个字节数组的操作。

  1. rune与字符处理:rune是int32的别名,用于表示一个Unicode码点(处理中文、emoji等多字节字符),底层会通过UTF-8解码将字节序列转换为rune序列。

二、核心函数底层实现详解

1. 基础判断类(HasPrefix/HasSuffix/Contains)

以strings.HasPrefix为例,底层逻辑非常简单:

Go
// 源码简化版
func HasPrefix(s, prefix string) bool {
// 1. 先判断长度:如果前缀长度 > 字符串长度,直接返回false
if len(prefix) > len(s) {
return false
}
// 2. 截取字符串前len(prefix)个字节,与前缀比较
return s[:len(prefix)] == prefix
}

  • 关键逻辑:直接通过字节切片的切片操作(s[:len(prefix)])截取对应长度,再逐字节比较。
  • 性能:时间复杂度O(n)(n为前缀长度),因为需要逐字节对比。

strings.Contains的底层实现(简化版):

Go
func Contains(s, substr string) bool {
// 调用Index函数,判断返回值是否 >=0
return Index(s, substr) != -1
}

// Index函数核心逻辑:暴力匹配算法(BF算法)
func Index(s, substr string) int {
n := len(substr)
switch {
case n == 0:
return 0
case n > len(s):
return -1
case n == 1:
// 单字符查找:直接遍历字节数组
return byteIndex(s, substr[0])
}
// 多字符查找:暴力匹配,逐个位置对比
for i := 0; i <= len(s)-n; i++ {
if s[i:i+n] == substr {
return i
}
}
return -1
}

  • 核心算法:短子串用暴力匹配(BF),优点是实现简单,缺点是最坏时间复杂度O(m*n)(m为原字符串长度,n为子串长度);Go 1.21+对长串优化了部分逻辑,但核心仍是字节级对比。

2. 字符串修改类(TrimSpace/ReplaceAll)

以strings.TrimSpace为例,底层逻辑:

Go
// 源码简化版
func TrimSpace(s string) string {
// 1. 转换为[]byte,方便操作
b := []byte(s)
// 2. 跳过开头的空白字符(空格、换行、制表符等)
start := 0
for start < len(b) && isSpace(b[start]) {
start++
}
// 3. 跳过结尾的空白字符
end := len(b)
for end > start && isSpace(b[end-1]) {
end--
}
// 4. 截取有效部分,转换回string
return string(b[start:end])
}

// 判断是否为空白字符(底层定义)
func isSpace(c byte) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'
}

  • 关键逻辑:

① 将字符串转为[]byte(因为string不可变,[]byte可修改);

② 双指针(start/end)跳过首尾空白;

③ 截取有效字节切片,转换回string(会创建新字符串)。

strings.ReplaceAll底层实现:

Go
// ReplaceAll等价于Replace(s, old, new, -1)
func ReplaceAll(s, old, new string) string {
return Replace(s, old, new, -1)
}

// Replace核心逻辑
func Replace(s, old, new string, n int) string {
// 1. 先判断old是否为空,或n=0,直接返回原字符串
if old == "" || n == 0 {
return s
}
// 2. 统计old出现的次数
count := 0
start := 0
for {
// 查找下一个old的位置
i := Index(s[start:], old)
if i == -1 || count >= n {
break
}
count++
start += i + len(old)
}
// 3. 如果没有匹配项,直接返回原字符串
if count == 0 {
return s
}
// 4. 计算新字符串长度,预分配[]byte(减少内存分配)
newLen := len(s) + count*(len(new)-len(old))
b := make([]byte, 0, newLen)
// 5. 逐段拼接:原字符串片段 + 替换后的字符串
start = 0
for i := 0; i < count; i++ {
j := Index(s[start:], old)
b = append(b, s[start:start+j]...)
b = append(b, new...)
start += j + len(old)
}
// 6. 拼接剩余部分
b = append(b, s[start:]...)
return string(b)
}

  • 核心优化:预分配[]byte的容量(newLen),避免多次扩容,提升性能;逐段拼接字节切片,最后转换为string。

3. 分割与拼接(Split/Join)

(1)strings.Split 底层实现

Go
// 源码简化版
func Split(s, sep string) []string {
return split(s, sep, -1)
}

func split(s, sep string, n int) []string {
// 1. 特殊情况:分隔符为空,按单个字符分割
if sep == "" {
return explode(s, n)
}
// 2. 统计分隔符出现的次数,预分配切片容量
count := 0
start := 0
for {
if n > 0 && count >= n-1 {
break
}
// 查找下一个分隔符位置
i := Index(s[start:], sep)
if i == -1 {
break
}
count++
start += i + len(sep)
}
// 3. 预分配切片,避免扩容
res := make([]string, 0, count+1)
// 4. 逐段截取字符串,存入切片
start = 0
for i := 0; i < count; i++ {
end := start + Index(s[start:], sep)
res = append(res, s[start:end])
start = end + len(sep)
}
// 5. 拼接最后一段
res = append(res, s[start:])
return res
}

  • 关键优化:先统计分隔符次数,预分配切片容量(count+1),减少append的扩容开销。

(2)strings.Join 底层实现

Go
// 源码简化版
func Join(a []string, sep string) string {
// 1. 特殊情况:空切片返回空字符串
if len(a) == 0 {
return ""
}
// 2. 只有一个元素,直接返回该元素
if len(a) == 1 {
return a[0]
}
// 3. 计算总长度:所有字符串长度 + 分隔符长度*(len(a)-1)
totalLen := 0
for _, s := range a {
totalLen += len(s)
}
totalLen += len(sep) * (len(a) - 1)
// 4. 预分配[]byte,逐段拼接
b := make([]byte, 0, totalLen)
b = append(b, a[0]...)
for _, s := range a[1:] {
b = append(b, sep...)
b = append(b, s...)
}
return string(b)
}

  • 核心优势:预计算总长度,一次性分配足够的字节切片,避免多次扩容,这也是strings.Join比直接用+拼接字符串效率高的原因(+拼接每次都会创建新字符串,多次内存分配)。

4. 大小写转换(ToUpper/ToLower)

以strings.ToLower为例,底层逻辑:

Go
// 源码简化版
func ToLower(s string) string {
// 1. 先检查是否需要转换:如果全是小写,直接返回原字符串
hasUpper := false
for i := 0; i < len(s); i++ {
if 'A' <= s[i] && s[i] <= 'Z' {
hasUpper = true
break
}
// 处理多字节字符(如中文、希腊字母)
if s[i] >= utf8.RuneSelf {
return toLowerRune(s)
}
}
if !hasUpper {
return s
}
// 2. 单字节字符(ASCII)直接转换
b := []byte(s)
for i := 0; i < len(b); i++ {
if 'A' <= b[i] && b[i] <= 'Z' {
b[i] += 'a' - 'A' // 大写转小写:ASCII码+32
}
}
return string(b)
}

// 处理多字节rune的大小写转换
func toLowerRune(s string) string {
var b []byte
for _, r := range s {
lowerRune := unicode.ToLower(r) // 调用unicode包的ToLower
if b == nil && lowerRune == r {
continue
}
if b == nil {
b = make([]byte, 0, len(s))
b = append(b, s[:len(s)-len([]rune{s[len(s):]})]...)
}
b = utf8.AppendRune(b, lowerRune)
}
if b == nil {
return s
}
return string(b)
}

  • 核心逻辑:

① ASCII字符(单字节)直接通过修改字节值(+32)转换;

② 多字节字符(如中文、非英语字符)通过unicode包的ToLower/ToUpper转换,底层依赖Unicode码点映射表。

三、底层实现的核心特点

  1. 优先操作[]byte:所有修改/遍历操作都会先将string转为[]byte,因为[]byte是可变的,且直接操作字节效率更高;
  2. 预分配内存:核心函数(Join、Split、Replace)都会先计算结果长度,预分配[]byte容量,避免多次扩容,提升性能;
  3. 区分单字节/多字节:对ASCII字符(单字节)做快速处理,对多字节字符(rune)通过UTF-8解码/编码处理;
  4. 惰性处理:很多函数会先检查是否需要操作(如ToLower先检查是否有大写字符),无需操作则直接返回原字符串,减少不必要的内存分配。

总结

  1. strings包底层核心是字节级操作:将string转为[]byte处理,利用预分配内存、惰性检查等优化性能;
  2. 核心优化点:预计算结果长度、预分配[]byte容量,避免多次内存扩容(这是strings.Join比+拼接快的关键);
  3. 字符处理逻辑:ASCII单字节字符直接修改字节值,多字节Unicode字符通过rune和unicode包处理。

理解这些底层实现,能帮助你在高性能场景下选择合适的字符串操作函数(比如用strings.Join代替+拼接)。

|(注:文档部分内容可能由 AI 生成)

附件原文:onenote-go-string-_Go_strings_.docx(点击下载)

内容反馈

发现错误或想补充内容?

GoClub Pro 是静态知识库,评论功能不再加载第三方脚本。你可以在 GitHub 仓库提交 Issue,反馈会直接进入项目协作流程。

提交 GitHub 反馈