go语言中的字符串详解
目录
字符串的基本特点
1.字符串的不可变性
2.其他基本特点
字符串基本操作
1. 创建字符串
2. 获取字符串长度
3. 字符串拼接
4. 遍历字符串
5. 字符串比较
字符串常用函数
1. 判断子串
2. 查找与索引
3. 字符串替换
4. 分割与连接
5. 修剪字符串
6. 大小写转换
字符串与其他类型转换
1. 字符串转数字
2. 字符串转字节切片
3. 字节切片转字符串
4. 字符串转 Rune 切片
性能优化建议
Go 语言中,字符串是不可变的 UTF-8 编码字节序列。它提供了多种字符串操作方法和包支持,以下是 Go 中字符串操作的详细讲解。
字符串的基本特点
1.字符串的不可变性
在 Go 中,字符串一旦被创建,其内容是无法被直接修改的。这一特性源于字符串在 Go 中被设计为只读的 UTF-8 字节序列。不可变字符串避免了多线程环境下的竞态条件,字符串可以安全地在不同 Goroutines 中传递而不需要额外的同步操作。字符串不可变意味着可以缓存其哈希值或其他元数据,从而在需要多次比较时提高效率(如在 map 中用作键)。
-
无法直接修改字符串内容 你可以通过索引访问字符串中的某个字节,但无法通过索引赋值来修改字符串内容。
s := "hello" fmt.Println(s[0]) // 输出 104,对应 'h' // s[0] = 'H' // 编译错误:cannot assign to s[0]
-
修改字符串会创建新的字符串 如果需要更改字符串的内容,实际上是通过创建一个新的字符串来实现的,而不是在原字符串上进行修改。
s := "hello" newS := "H" + s[1:] // 创建新字符串 fmt.Println(newS) // 输出 "Hello"
-
性能的隐性影响
- 因为字符串不可变,每次修改字符串时都会分配新的内存来存储新字符串,频繁的字符串操作可能会导致性能问题。
- 解决方法:对于频繁的拼接或修改操作,可以使用可变的
strings.Builder
或[]byte
。
2.其他基本特点
- 索引访问:可以通过索引访问字节(
s[i]
),但无法直接修改。 - 支持 Unicode:字符串是 UTF-8 编码,可以包含中文、日文等多字节字符。
- 零值:字符串的零值是空字符串
""
。
字符串基本操作
1. 创建字符串
s1 := "Hello, World!" // 直接赋值
s2 := `这是一个
多行字符串` // 原生字符串(支持多行,内容不转义)
2. 获取字符串长度
len()
返回字符串的字节数(注意是字节数,不是字符数)。
s := "你好"
fmt.Println(len(s)) // 输出 6,因为 "你" 和 "好" 各占 3 个字节
3. 字符串拼接
使用 +
或 fmt.Sprintf
拼接字符串。
s1 := "Hello"
s2 := "World"
s := s1 + ", " + s2
fmt.Println(s) // 输出 "Hello, World"
s3 := fmt.Sprintf("%s, %s!", s1, s2)
fmt.Println(s3) // 输出 "Hello, World!"
4. 遍历字符串
s := "你好Go"
for i := 0; i < len(s); i++ {
fmt.Printf("Byte %d: %x\n", i, s[i]) // 按字节遍历
}
for i, r := range s {
fmt.Printf("Index %d: Rune %c\n", i, r) // 按字符遍历
}
5. 字符串比较
s1 := "abc"
s2 := "def"
fmt.Println(s1 == s2) // false
fmt.Println(s1 < s2) // true,按字典序比较
字符串常用函数
Go 提供了内置的 strings
包来操作字符串。
1. 判断子串
strings.Contains
:是否包含子串。strings.HasPrefix
:是否以子串开头。strings.HasSuffix
:是否以子串结尾。
import "strings"
s := "hello, world"
fmt.Println(strings.Contains(s, "world")) // true
fmt.Println(strings.HasPrefix(s, "hello")) // true
fmt.Println(strings.HasSuffix(s, "d")) // true
2. 查找与索引
strings.Index
:返回子串首次出现的索引,未找到返回 -1。strings.LastIndex
:返回子串最后出现的索引。strings.Count
:统计子串出现的次数。
s := "go is a good language"
fmt.Println(strings.Index(s, "good")) // 8
fmt.Println(strings.LastIndex(s, "g")) // 17
fmt.Println(strings.Count(s, "g")) // 3
3. 字符串替换
strings.Replace
:替换指定子串。strings.ReplaceAll
:替换所有子串。
s := "hello, hello, world"
s1 := strings.Replace(s, "hello", "hi", 1) // 替换 1 次
s2 := strings.ReplaceAll(s, "hello", "hi") // 替换所有
fmt.Println(s1) // "hi, hello, world"
fmt.Println(s2) // "hi, hi, world"
4. 分割与连接
strings.Split
:根据分隔符分割。strings.Join
:将字符串切片连接。
s := "a,b,c"
parts := strings.Split(s, ",")
fmt.Println(parts) // [a b c]
joined := strings.Join(parts, "-")
fmt.Println(joined) // "a-b-c"
5. 修剪字符串
strings.Trim
:移除指定字符。strings.TrimSpace
:移除两端空格。strings.TrimPrefix
和strings.TrimSuffix
:移除前缀或后缀。
s := " hello "
fmt.Println(strings.TrimSpace(s)) // "hello"
fmt.Println(strings.Trim(s, " h")) // "ello"
fmt.Println(strings.TrimPrefix(s, " ")) // "hello "
fmt.Println(strings.TrimSuffix(s, " ")) // " hello"
6. 大小写转换
strings.ToUpper
:转大写。strings.ToLower
:转小写。
s := "GoLang"
fmt.Println(strings.ToUpper(s)) // "GOLANG"
fmt.Println(strings.ToLower(s)) // "golang"
字符串与其他类型转换
1. 字符串转数字
使用 strconv
包。
import "strconv"
n, err := strconv.Atoi("123")
if err != nil {
fmt.Println("转换错误")
}
fmt.Println(n) // 123
s := strconv.Itoa(456)
fmt.Println(s) // "456"
2. 字符串转字节切片
s := "hello"
b := []byte(s)
fmt.Println(b) // [104 101 108 108 111]
3. 字节切片转字符串
b := []byte{104, 101, 108, 108, 111}
s := string(b)
fmt.Println(s) // "hello"
4. 字符串转 Rune 切片
s := "你好"
r := []rune(s)
fmt.Println(r) // [20320 22909]
fmt.Println(string(r)) // "你好"
性能优化建议
- 拼接大量字符串时:使用
strings.Builder
或bytes.Buffer
,效率高于+
拼接。
import "strings"
var builder strings.Builder
builder.WriteString("hello")
builder.WriteString(", ")
builder.WriteString("world")
fmt.Println(builder.String()) // "hello, world"
- 避免重复操作:对字符串多次操作时,优先考虑转换为字节切片或 rune 切片操作。