字符串与字节切片转换
本教程共 80 篇 · 第 34 篇 · 更新于 2026-07-27 · 约 8 分钟阅读
34. 字符串与字节切片转换
本节目标:搞懂 string 和 []byte 的互转原理,学会用 strings 和 strconv 包处理字符串。
string 和 []byte 的关系
Go 的字符串底层就是一个只读的字节序列。[]byte 是可写的字节切片。两者之间的转换非常常见:
package main
import "fmt"
func main() {
s := "Hello"
b := []byte(s) // string 转 []byte
fmt.Println(b) // [72 101 108 108 111]
b[0] = 104 // 改成小写h
s2 := string(b) // []byte 转 string
fmt.Println(s2) // hello
}
转换的代价
每次 string 转 []byte 或反过来,都会发生内存拷贝。因为 string 是不可变的,[]byte 是可变的,为了保证安全,Go 必须复制一份数据。
s := "Hello World"
b := []byte(s) // 拷贝了一份
在循环里频繁转换会拖慢性能。如果你只是想读字符串的字节,不需要修改,可以用 unsafe 包的零拷贝转换,但这属于高级技巧,日常不建议用。
Tip编译器做了优化:在某些场景下(比如
for i := 0; i < len(s); i++里用s[i]),编译器会自动避免拷贝。但你用[]byte(s)显式转换时,拷贝是真实发生的。
strings 包常用函数
strings 包提供了一堆字符串操作函数,日常开发几乎天天用。
判断类
package main
import (
"fmt"
"strings"
)
func main() {
s := "Hello, World"
fmt.Println(strings.Contains(s, "World")) // true 是否包含
fmt.Println(strings.HasPrefix(s, "Hello")) // true 是否以...开头
fmt.Println(strings.HasSuffix(s, "World")) // true 是否以...结尾
fmt.Println(strings.EqualFold("HELLO", "hello")) // true 忽略大小写比较
}
查找类
fmt.Println(strings.Index(s, "World")) // 7 第一次出现的下标
fmt.Println(strings.LastIndex(s, "l")) // 10 最后一次出现的下标
fmt.Println(strings.Count(s, "l")) // 3 出现次数
替换和分割
// 替换:-1表示全部替换
fmt.Println(strings.Replace(s, "l", "L", 1)) // HeLlo, World
fmt.Println(strings.ReplaceAll(s, "l", "L")) // HeLLo, WorLd
// 分割
parts := strings.Split("a,b,c,d", ",")
fmt.Println(parts) // [a b c d]
// 拼接
fmt.Println(strings.Join([]string{"a", "b", "c"}, "-")) // a-b-c
大小写和去空格
fmt.Println(strings.ToUpper(s)) // HELLO, WORLD
fmt.Println(strings.ToLower(s)) // hello, world
fmt.Println(strings.TrimSpace(" hi ")) // hi 去首尾空白
fmt.Println(strings.Trim("xxhixx", "x")) // hi 去首尾指定字符
Note
strings.TrimSpace去的是首尾空白字符(空格、tab、换行等),strings.Trim可以自定义要去的字符。这两个很容易搞混。
strings.Builder 高效拼接
在循环里拼接字符串,不要用 +,每次 + 都会分配新内存。用 strings.Builder:
package main
import (
"fmt"
"strings"
)
func main() {
var builder strings.Builder
for i := 0; i < 100; i++ {
builder.WriteString("hello")
}
result := builder.String()
fmt.Println(len(result)) // 500
}
strings.Builder 内部维护一个字节缓冲区,写入时不会反复分配内存,性能比 + 好得多。
strconv 包:字符串和其他类型转换
strconv 包负责字符串和基本类型之间的转换。
字符串转数字
package main
import (
"fmt"
"strconv"
)
func main() {
// string 转 int
n, err := strconv.Atoi("42")
fmt.Println(n, err) // 42 <nil>
// int 转 string
s := strconv.Itoa(42)
fmt.Println(s) // 42
// string 转 float64
f, err := strconv.ParseFloat("3.14", 64)
fmt.Println(f, err) // 3.14 <nil>
// bool 转 string
fmt.Println(strconv.FormatBool(true)) // true
// string 转 bool
b, err := strconv.ParseBool("true")
fmt.Println(b, err) // true <nil>
}
Warning
Atoi和ParseFloat转换失败时会返回错误,一定要检查。不要假设输入一定是合法的数字字符串。
n, err := strconv.Atoi("abc")
if err != nil {
fmt.Println("转换失败:", err) // 转换失败: strconv.Atoi: parsing "abc": invalid syntax
}
Format 系列函数
数字转字符串除了 Itoa,还有 FormatFloat 和 FormatInt:
// 进制转换
fmt.Println(strconv.FormatInt(255, 16)) // ff 十六进制
fmt.Println(strconv.FormatInt(255, 2)) // 11111111 二进制
// 反过来
n, _ := strconv.ParseInt("ff", 16, 64)
fmt.Println(n) // 255
小结
字符串操作的核心知识点:
string和[]byte互转会拷贝,循环里要注意性能strings包:Contains、Index、Replace、Split、Join、Builderstrconv包:Atoi/Itoa 互转、ParseFloat、FormatInt 进制转换
下一章介绍 Go 1.21 新增的 maps 标准库和 cmp 包,map 操作也能一行搞定了。