首页 / Go 语言入门教程 / 字符串与字节切片转换

Go 语言入门教程

字符串与字节切片转换

本教程共 80 篇 · 第 34 篇 · 更新于 2026-07-27 · 约 8 分钟阅读

GoGo 入门教程字符串stringbytestringsstrconv

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

AtoiParseFloat 转换失败时会返回错误,一定要检查。不要假设输入一定是合法的数字字符串。

n, err := strconv.Atoi("abc")
if err != nil {
    fmt.Println("转换失败:", err) // 转换失败: strconv.Atoi: parsing "abc": invalid syntax
}

Format 系列函数

数字转字符串除了 Itoa,还有 FormatFloatFormatInt

// 进制转换
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、Builder
  • strconv 包:Atoi/Itoa 互转、ParseFloat、FormatInt 进制转换

下一章介绍 Go 1.21 新增的 maps 标准库和 cmp 包,map 操作也能一行搞定了。