此专题是因为自己在刷题时老是对string利用不当,特地整理
string
以下内容来自gpt
在 Go 语言中,字符串(string)是一个包含了一系列字符(Unicode码点)的不可变序列。字符串在 Go 语言中是一个内置的数据类型,使用起来非常方便。以下是关于 Go 语言中字符串的一些详细说明:
不可变性:字符串是不可变的,这意味着一旦创建了一个字符串,就无法直接修改其内容。如果需要修改字符串,通常需要将其转换为可变的字节切片,进行修改后再转换回字符串。
字节表示:字符串实际上是一个字节切片(
[]byte
)的只读视图。每个字符在字符串中占用的字节数取决于该字符的 Unicode 编码。在字符串中,每个 Unicode 码点都可能占用多个字节。Unicode 支持:Go 的字符串类型支持 Unicode,这意味着它可以存储任何 Unicode 字符,包括中文、日文、希腊字母等等。
字面值表示:在 Go 中,用双引号
"
包裹的字符序列表示一个字符串字面值。例如:"Hello, 世界!"
。另外,还可以使用反引号`
包裹的字符序列,这种方式表示的字符串会保留其原始格式,包括换行符和特殊字符。字符串连接:可以使用加号
+
来连接字符串,例如:"Hello" + " " + "World"
。长度:通过内置函数
len()
可以获取字符串的长度,即其中字符的数量。索引访问:可以通过索引来访问字符串中的单个字符。在 Go 中,字符串是 UTF-8 编码的,因此一个字符可能由多个字节组成。可以使用索引访问字符串中的单个字节,也可以使用 Unicode 码点的索引访问字符。例如,
str[0]
可以获取字符串中的第一个字节,而rune(str[0])
可以将其转换为 Unicode 码点。字符串迭代:可以使用
for range
循环来迭代字符串中的 Unicode 字符。每次迭代,range
会返回当前字符的 Unicode 码点及其字节位置。字符串切片:可以使用切片操作来获取字符串的子串。例如,
str[1:4]
可以获取字符串中从索引 1 到索引 3(不包含索引 4)的子串。字符串比较:可以使用
==
和!=
操作符来比较两个字符串是否相等。此外,可以使用<
、<=
、>
、>=
操作符进行字符串的大小比较,这些比较会按照字典顺序进行。
总的来说,Go 语言中的字符串类型提供了丰富的功能和便捷的操作,使得处理文本数据变得非常简单和高效。
package main
import (
"fmt"
)
func main() {
s := "你好,世界!"
// 使用 range 遍历字符串,x 是一个 Unicode 字符
for _, x := range s {
fmt.Printf("字符:%c,ASCII 码值:%d\n", x, x)
}
}
字符:你,ASCII 码值:20320
字符:好,ASCII 码值:22909
字符:,,ASCII 码值:65292
字符:世,ASCII 码值:19990
字符:界,ASCII 码值:30028
字符:!,ASCII 码值:65281
package main
import (
"fmt"
)
func main() {
s := "你好,世界!"
// 使用索引遍历字符串,s[i] 是一个字节
for i := 0; i < len(s); i++ {
fmt.Printf("字节:%c,ASCII 码值:%d\n", s[i], s[i])
}
}
字节:你,ASCII 码值:228
字节:好,ASCII 码值:229
字节:,,ASCII 码值:239
字节:世,ASCII 码值:199
字节:界,ASCII 码值:191
字节:!,ASCII 码值:161
在 Go 语言中,x
是 rune
类型,而 s[i]
是 byte
类型。
-
rune
类型用于表示 Unicode 码点,它是int32
的别名。在遍历字符串时,range
关键字会自动将字符串转换为rune
类型,这样可以确保正确处理 Unicode 字符。 -
byte
类型用于表示单个字节,即 8 位二进制数。在遍历字符串时,按索引访问字符串会得到该位置的字节值。
所以,在使用 for _, x := range s
遍历字符串时,x
是一个 Unicode 字符,类型为 rune
。而在使用 for i := 0; i < len(s); i++ { s[i] }
的方式中,s[i]
是一个字节,类型为 byte
。
对于string而言有个最重要的特性不可变性
在 Go 语言中,字符串是不可变的,这意味着一旦创建了一个字符串,就无法直接修改其内容。这是因为字符串在内存中是以只读的方式存储的,即字符串的内容是不可更改的。这个特性带来了一些好处和挑战:
好处:
- 线程安全: 字符串的不可变性使得在多线程环境下对字符串的访问更加安全,无需担心并发修改带来的问题。
- 内存优化: 由于字符串是不可变的,可以在内存中共享相同的字符串值,节省内存空间。
挑战:
- 效率问题: 每次对字符串进行修改时,都需要创建一个新的字符串,而不是直接在原字符串上进行修改。这样可能会导致内存开销较大,尤其是对于频繁修改的场景。
- 拷贝问题: 当对字符串进行切片或者拼接操作时,可能会导致底层数据的拷贝,从而增加额外的开销。
解决办法:
尽管字符串是不可变的,但是在实际开发中,我们可以采取一些策略来解决字符串不可变性带来的问题:
使用字节切片: 如果需要频繁地对字符串进行修改,可以将字符串转换为可变的字节切片(
[]byte
),然后在切片上进行修改。修改完成后,可以再将字节切片转换回字符串。需要注意的是,这样做会导致额外的内存开销和拷贝操作。使用 strings 包中的函数: Go 标准库的
strings
包提供了许多用于处理字符串的函数,这些函数通常会返回一个新的字符串而不是修改原始字符串。使用这些函数可以避免直接修改字符串的需求。使用字符串构建器(strings.Builder): 如果需要进行大量的字符串拼接操作,可以使用
strings.Builder
类型来构建字符串。strings.Builder
类型提供了高效的字符串拼接功能,避免了频繁的字符串拷贝操作。避免频繁修改字符串: 尽量设计避免频繁修改字符串的算法和数据结构,可以通过预先分配足够的内存空间或者使用合适的数据结构来降低字符串的修改次数。
综上所述,虽然字符串是不可变的,但是在实际应用中,通过合理的设计和使用适当的方法,我们可以有效地处理字符串,并且避免不可变性带来的问题。
下面是举几个例子来详细说明解决字符串不可变性带来的问题以及解决办法:
1. 需要修改字符串中的某个字符
假设我们有一个字符串,需要将其中某个字符替换为另一个字符。由于字符串是不可变的,直接修改字符串是不允许的。我们可以使用字节切片来解决这个问题:
package main
import "fmt"
func main() {
s := "hello"
// 将字符串转换为字节切片
b := []byte(s)
// 修改字节切片中的某个字节
b[0] = 'H'
// 将字节切片转换回字符串
s = string(b)
fmt.Println(s) // 输出:Hello
}
在这个例子中,我们将字符串 "hello" 转换为字节切片 b
,然后修改了切片中的第一个字节,最后将修改后的字节切片重新转换回字符串。
2. 需要进行字符串拼接操作
假设我们需要对多个字符串进行拼接操作,但由于字符串不可变性,每次拼接字符串都会创建一个新的字符串,导致性能下降。我们可以使用 strings.Builder
类型来构建字符串:
package main
import (
"fmt"
"strings"
)
func main() {
var builder strings.Builder
// 构建字符串
builder.WriteString("Hello, ")
builder.WriteString("World!")
// 获取拼接后的字符串
result := builder.String()
fmt.Println(result) // 输出:Hello, World!
}
在这个例子中,我们使用 strings.Builder
类型来构建字符串,使用 WriteString
方法进行字符串的拼接操作。最后通过 String
方法获取拼接后的最终字符串。
3. 遍历字符串并统计字符出现次数
假设我们需要遍历字符串并统计其中每个字符的出现次数。我们可以使用 map[rune]int
类型来统计字符出现次数:
package main
import (
"fmt"
)
func main() {
s := "hello"
// 使用 map 统计字符出现次数
freq := make(map[rune]int)
for _, r := range s {
freq[r]++
}
// 输出字符出现次数
for char, count := range freq {
fmt.Printf("字符 %c 出现了 %d 次\n", char, count)
}
}
在这个例子中,我们使用 map[rune]int
类型来统计字符出现次数,rune
类型用于表示 Unicode 码点,这样可以正确处理字符串中的 Unicode 字符。遍历字符串时,将每个字符作为键存储在 freq
map 中,并统计其出现次数。
通过这些例子,我们可以看到如何使用不同的方法来解决字符串不可变性带来的问题,从而更有效地处理字符串。
strings常用函数
在 Go 语言的 strings
包中有许多常用的函数,用于处理字符串。以下是其中几个常用函数的介绍:
len(s string) int
: 返回字符串s
的字节数(即字符串的长度)。注意,由于 Go 中的字符串是 UTF-8 编码的,一个 Unicode 字符可能占用多个字节,因此len()
返回的是字符串的字节数,而不是字符数。strings.Contains(s, substr string) bool
: 判断字符串s
是否包含子串substr
,如果包含则返回true
,否则返回false
。strings.HasPrefix(s, prefix string) bool
和strings.HasSuffix(s, suffix string) bool
: 分别用于判断字符串s
是否以指定的前缀prefix
或后缀suffix
开始或结束,如果是则返回true
,否则返回false
。strings.Index(s, substr string) int
和strings.LastIndex(s, substr string) int
: 分别返回字符串s
中子串substr
的第一次和最后一次出现的索引位置(如果没有找到则返回 -1)。strings.Replace(s, old, new string, n int) string
: 将字符串s
中的所有子串old
替换为子串new
,如果指定了n
,则最多替换n
个匹配项。返回替换后的新字符串。strings.Split(s, sep string) []string
: 使用分隔符sep
将字符串s
分割成多个子串,并返回一个切片。如果sep
为空,则将字符串切分为每个 Unicode 字符一个子串。strings.ToLower(s string) string
和strings.ToUpper(s string) string
: 分别将字符串s
中的所有字符转换为小写或大写,并返回新的字符串。strings.TrimSpace(s string) string
: 去除字符串s
前后的所有空白字符,并返回新的字符串。空白字符包括空格、制表符、换行符等。
这些函数是 strings
包中的一部分,可以方便地进行字符串处理,常用于文本处理、字符串匹配、分割、替换等操作。在实际开发中,经常会用到这些函数来处理字符串数据。
strconv常用函数
在 Go 语言的 strconv
包中提供了一系列用于字符串和基本数据类型之间转换的函数。以下是 strconv
包中一些常用的函数:
strconv.Atoi(s string) (int, error)
: 将字符串s
转换为整数int
。如果转换成功,返回转换后的整数和nil
;如果转换失败,则返回0
和一个非nil
的错误。strconv.ParseInt(s string, base int, bitSize int) (int64, error)
: 将字符串s
解析为指定进制的整数int64
。参数base
指定了进制,可以是0
表示自动推断进制,或者2
、8
、10
或16
。参数bitSize
指定了结果的位大小(0
、8
、16
、32
或64
)。strconv.ParseFloat(s string, bitSize int) (float64, error)
: 将字符串s
解析为浮点数float64
。参数bitSize
指定了结果的精度(32 或 64)。如果转换成功,返回转换后的浮点数和nil
;如果转换失败,则返回0
和一个非nil
的错误。strconv.Itoa(i int) string
: 将整数i
转换为字符串。该函数是strconv.Itoa(i)
的简写形式。strconv.FormatInt(i int64, base int) string
和strconv.FormatFloat(f float64, fmt byte, prec, bitSize int) string
: 分别用于将整数i
和浮点数f
格式化为字符串。参数base
和fmt
分别表示进制和格式,prec
表示精度,bitSize
表示位大小。strconv.Quote(s string) string
和strconv.QuoteToASCII(s string) string
: 将字符串s
转换为带引号的字符串。Quote
函数会按照 Go 语法转义非 ASCII 字符,而QuoteToASCII
函数会将非 ASCII 字符转换为\u
格式的 Unicode 转义序列。strconv.Unquote(s string) (string, error)
: 将带引号的字符串s
转换为原始字符串。如果转换成功,返回去除引号后的字符串和nil
;如果转换失败,则返回s
和一个非nil
的错误。strconv.AppendInt(dst []byte, i int64, base int) []byte
和strconv.AppendFloat(dst []byte, f float64, fmt byte, prec, bitSize int) []byte
: 分别用于将整数i
和浮点数f
追加到字节切片dst
中,返回追加后的字节切片。
这些函数是 strconv
包中一些常用的函数,用于字符串和基本数据类型之间的转换。在处理文本数据、解析用户输入等场景中,这些函数经常会被使用到。