一、Go数据结构-数组基本使用

1.1 什么是数组

在 Go 语言中,数组(Array)是一种用于存储一组相同类型元素的数据结构。数组的长度是固定的,一旦定义后,其大小不能改变。数组在 Go 中是值类型,这意味着当你将一个数组赋值给另一个数组时,会进行一次数组内容的复制。

1.2 声明数组

在 Go 中声明数组需要指定数组的长度和元素类型。

声明数组格式: var 数组名字 = [数组长度]数组类型{数组的数据}

下面定义关于老师名字的数组,将下面代码粘贴到00-Declare-Array-1.go文件中并保存该文件

package main

import "fmt"

func main() {
    var teacherNameArray = [3]string{"张三", "王五", "李四"}
    fmt.Println(teacherNameArray)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这里导入了 "fmt" 包,用于格式化输出。

部分 2 - 声明数组

var teacherNameArray = [3]string{"张三", "王五", "李四"}

这行代码声明了一个包含三个字符串元素的数组 teacherNameArray。使用 [3]string 表示数组类型,然后在花括号内指定初始值。

部分 3 - 输出数组内容

fmt.Println(teacherNameArray)

使用 fmt.Println() 函数输出数组的内容,结果会显示整个数组的元素。

运行上面代码

$ go run .\00-Declare-Array-1.go
[张三 王五 李四]

除了上面方法声明数组外,我们还可以使用语法糖来声明数组。下面定义关于老师年龄的数组,将下面代码粘贴到00-Declare-Array-2.go文件中并保存该文件

package main

import "fmt"

func main() {
    teacherAgeArray := [3]int{18, 19, 20}
    fmt.Println(teacherAgeArray)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这里导入了 "fmt" 包,用于格式化输出。

部分 2 - 声明数组

teacherAgeArray := [3]int{18, 19, 20}

这行代码使用简短变量声明方式声明了一个包含三个整数元素的数组 teacherAgeArray。使用 [3]int 表示数组类型,然后在花括号内指定初始值

部分 3 - 输出数组内容

fmt.Println(teacherAgeArray)

运行上面代码

$ go run .\00-Declare-Array-2.go
[18 19 20]

1.3 数组元素

1.3.1 访问数组元素

在 Go 语言中,通过索引来访问数组元素是一种常见的操作。数组索引从 0 开始,表示第一个元素,依次递增。

访问数组元素格式: 数组名字[索引]

下面定义关于老师名字的数组,并通过索引访问数组元素,将下面代码粘贴到01-AccessArrayElements.go文件中并保存该文件

package main

import "fmt"

func main() {
    var teacherNameArray = [3]string{"张三", "王五", "李四"}
    fmt.Println("第一位老师的名字: ", teacherNameArray[0])
    fmt.Println("第二位老师的名字: ", teacherNameArray[1])
    fmt.Println("第三位老师的名字: ", teacherNameArray[2])
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这里导入了 "fmt" 包,用于格式化输出

部分 2 - 声明数组并初始化

var teacherNameArray = [3]string{"张三", "王五", "李四"}

这行代码声明了一个包含三个字符串元素的数组 teacherNameArray,并通过初始化赋予了元素初始值。

部分 3 - 访问数组元素

fmt.Println("第一位老师的名字: ", teacherNameArray[0])
fmt.Println("第二位老师的名字: ", teacherNameArray[1])
fmt.Println("第三位老师的名字: ", teacherNameArray[2])

通过索引 0、1 和 2 访问数组中的不同元素,并输出对应的老师名字。

运行上面代码

$ go run .\01-AccessArrayElements.go
第一位老师的名字:  张三
第二位老师的名字:  王五
第三位老师的名字:  李四

1.3.2 修改数组元素

在 Go 语言中,修改数组元素是一种常见的操作,可以通过索引来访问并更新数组中的特定元素。

修改数组元素格式: 数组名字[索引]=新元素

下面定义关于老师名字的数组,并通过索引修改数组元素,将下面代码粘贴到02-ModifyArrayElements.go文件中并保存该文件

package main

import "fmt"

func main() {
    var teacherNameArray = [3]string{"张三", "王五", "李四"}
    teacherNameArray[0] = "张武"
    fmt.Println("第一位老师的新的名字: ", teacherNameArray[0])
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这里导入了 "fmt" 包,用于格式化输出

部分 2 - 声明数组并初始化

var teacherNameArray = [3]string{"张三", "王五", "李四"}

这行代码声明了一个包含三个字符串元素的数组 teacherNameArray,并通过初始化赋予了元素初始值。

部分 3 - 修改数组元素

teacherNameArray[0] = "张武"

使用索引 0 来修改数组中的第一个元素,将其值从 "张三" 修改为 "张武"。

部分 4 - 输出修改后的元素

fmt.Println("第一位老师的新的名字: ", teacherNameArray[0])

输出修改后的第一个老师的名字。

运行上面代码

$ go run .\02-ModifyArrayElements.go
第一位老师的新的名字:  张武

1.4 数组长度

1.4.1 获取数组长度

在 Go 语言中,数组的长度是固定的,即在声明数组时就需要指定数组的长度。数组的长度表示数组可以容纳的元素数量,它是数组类型的一部分。

获取数组长度格式: len(数组名字)

下面定义关于老师名字的数组,并通过len获取数组长度,将下面代码粘贴到03-ArrayLength-1.go文件中并保存该文件

package main

import "fmt"

func main() {
    var teacherNameArray = [3]string{"张三", "王五", "李四"}
    fmt.Println("teacherNameArray数组长度: ", len(teacherNameArray))
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这里导入了 "fmt" 包,用于格式化输出

部分 2 - 声明数组并初始化

var teacherNameArray = [3]string{"张三", "王五", "李四"}

这行代码声明了一个包含三个字符串元素的数组 teacherNameArray,并通过初始化赋予了元素初始值。

部分 3 - 获取数组长度

fmt.Println("teacherNameArray数组长度: ", len(teacherNameArray))

使用 len() 函数来获取数组 teacherNameArray 的长度,并将结果输出。

运行上面代码

$ go run .\03-ArrayLength-1.go
teacherNameArray数组长度:  3

1.4.2 自动推断数组长度

在 Go 语言中,数组的长度可以被自动推断,也就是说你不必明确地指定数组的长度,而是可以让编译器根据初始化值的个数来推断数组的长度。这种特性使得数组的初始化更加方便,尤其是在元素数量可能变化的情况下。

在声明数组的时候,可以使用 ... 来让编译器根据提供的初始化值来自动推断数组的长度。

将下面代码粘贴到03-ArrayLength-2.go文件中并保存该文件

package main

import "fmt"

func main() {
    teacherNameArray := [...]string{"张三", "王五", "李四"}
    fmt.Println(teacherNameArray)
    fmt.Println("数组长度:", len(teacherNameArray))

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这里导入了 "fmt" 包,用于格式化输出

部分 2 - main函数

func main() {
    // 代码部分
}

这是程序入口,所有的代码都将在这个函数中执行。

部分 3 - 数组定义和初始化

teacherNameArray := [...]string{"张三", "王五", "李四"}

定义了一个包含三个字符串元素的数组 teacherNameArray,并使用自动推断数组长度的方式进行了初始化。

部分 4 - 打印整个数组

fmt.Println(teacherNameArray)

使用 fmt.Println() 函数来打印整个数组 teacherNameArray,将数组的所有元素一次性输出。

部分 5 - 获取数组长度并打印

fmt.Println("数组长度:", len(teacherNameArray))

使用 len() 函数来获取数组 teacherNameArray 的长度,这个长度会根据你提供的初始化值的个数进行推断。然后,你使用fmt.Println() 函数来打印数组的长度。

运行上面代码

$ go run .\03-ArrayLength-2.go
[张三 王五 李四]
数组长度: 3

1.5 遍历数组

在 Go 语言中,遍历数组是指逐个访问数组中的元素。遍历数组通常使用循环结构来实现,以便访问数组中的每个元素并执行相应的操作。

下面使用 for 循环来遍历数组,将下面代码粘贴到04-Iterate-through-array-1.go文件中并保存该文件

package main

import "fmt"

func main() {
    var teacherNameArray = [3]string{"张三", "王五", "李四"}
    for i := 0; i < len(teacherNameArray); i++ {
        fmt.Printf("第%d个数据为:%s\n", i+1, teacherNameArray[i])
    }

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这里导入了 "fmt" 包,用于格式化输出

部分 2 - 主函数

func main() {
    // 代码内容
}

这是程序的入口函数,所有的代码都会在这里执行。

部分 3 - 声明和初始化数组

var teacherNameArray = [3]string{"张三", "王五", "李四"}

在主函数中,声明了一个包含三个字符串元素的数组 teacherNameArray,并使用字面值初始化了数组的元素。

部分 4 - 使用for循环遍历数组

for i := 0; i < len(teacherNameArray); i++ {
    fmt.Printf("第%d个数据为:%s\n", i+1, teacherNameArray[i])
}

在循环内部,使用 for 循环遍历数组的每个元素。循环变量 i 从 0 开始,逐渐增加,直到达到数组长度(len(teacherNameArray))为止。在循环体内,使用 fmt.Printf() 格式化输出了每个元素的索引和值。

运行上面代码

$ go run .\04-Iterate-through-array-1.go
第1个数据为:张三
第2个数据为:王五
第3个数据为:李四

除了上面使用 for 循环来遍历数组外,也可以使用for-range循环遍历,将下面代码粘贴到04-Iterate-through-array-2.go文件中并保存该文件

package main

import "fmt"

func main() {
    var teacherNameArray = [3]string{"张三", "王五", "李四"}
    for key, value := range teacherNameArray {
        fmt.Printf("第%d个数据为:%s\n", key+1, value)
    }

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这里导入了 "fmt" 包,用于格式化输出

部分 2 - main函数

func main() {
    // 代码部分
}

这是程序入口,所有的代码都将在这个函数中执行。

部分 3 - 数组定义

var teacherNameArray = [3]string{"张三", "王五", "李四"}

定义了一个包含三个字符串元素的数组 teacherNameArray,其中每个字符串是老师的名字。

部分 4 - for循环和range

for key, value := range teacherNameArray {
    // 代码部分
}

这是一个 for 循环,使用 range 关键字遍历数组 teacherNameArray 中的每个元素。在每次迭代中,key 表示当前元素的索引,value 表示当前元素的值。

部分 5 - 输出信息

fmt.Printf("第%d个数据为:%s\n", key+1, value)

在循环体内,使用 fmt.Printf() 来格式化输出每个元素的索引(通过 key+1 加1以得到人类可读的编号)和值。

部分 6 - main函数闭合

}

代码结束于这里,表示 main 函数的结束。

运行上面代码

$ go run .\04-Iterate-through-array-2.go
第1个数据为:张三
第2个数据为:王五
第3个数据为:李四

1.6 学习数组注意事项

学习 Go 语言的数组时,以下几点是需要注意的:

  • 数组长度固定: 数组在声明后其长度就是固定的,无法在运行时改变。因此,在选择数组作为数据结构时,需要确保数组的长度能够满足你的需求。

  • 数组索引从 0 开始: 数组的索引是从 0 开始的,即第一个元素的索引为 0,第二个元素的索引为 1,以此类推。要注意索引越界问题,避免访问不存在的索引。

  • 数组是值类型: 在赋值操作中,数组会被复制。这意味着当将一个数组赋值给另一个数组时,会创建一个新的副本,而不是引用原始数组。这可能会导致性能问题,特别是在处理大型数组时。

  • 数组长度是类型的一部分: 在 Go 中,数组的长度也是数组类型的一部分。例如,[5]int[10]int 是不同的类型。这也意味着你不能将长度不同的数组直接进行赋值或比较。

  • 使用切片进行动态操作: 如果需要动态大小的数据结构,可以使用切片(Slice)。切片是对数组的抽象,可以更灵活地操作数据。切片允许你动态添加、删除元素,具有更好的扩展性。

  • 遍历数组: 使用 for 循环遍历数组时,注意循环变量的范围和索引的限制,避免索引越界错误。

  • 内存使用: 数组在内存中占用连续的存储空间。在处理大型数组时,可能会受到内存使用方面的限制。要谨慎处理内存,避免不必要的资源浪费。

  • 初始化和默认值: 在声明数组后,如果不显式地初始化数组元素,它们会被初始化为对应类型的默认值(例如,整数数组的元素默认为 0)。

  • 选择合适的数据结构: 虽然数组是一种基本的数据结构,但在实际开发中,根据具体需求选择合适的数据结构是很重要的。有时切片、映射(Map)或其他数据结构可能更适合特定的场景。

二、Go数据结构-切片基本使用

主要以下几方面介绍Go语言中切片:

  • 什么是切片
  • 访问元素
  • 修改元素
  • 添加元素
  • 创建切片
  • 切片长度和容量
  • 切片截取
  • 删除元素
  • 深拷贝
  • 浅拷贝

2.1 什么是切片

Go语言中的切片(Slice)是一种强大且灵活的数据结构,用于存储一系列相同类型的元素。切片可以看作是对底层数组的一个引用,它提供了动态大小、方便操作以及自动扩容的能力。

2.2 访问元素

切片的元素可以通过索引来访问,索引从0开始,以元素的顺序递增。

访问元素的基本语法:

// 假设有一个切片或数组 slice
element := slice[index]
  • element :你要访问的元素
  • index :元素的索引

将下面代码粘贴到00-BasicSliceOperations-01.go文件中并保存该文件

package main

import "fmt"

func main() {
    var s1 []int
    s2 := []int{1, 2, 3}
    fmt.Println("最初的切片数据是: ", s1)
    fmt.Println("访问的切片第一个元素是: ", s2[0])

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包和主函数

package main

import "fmt"

在这部分,代码声明了包名为 "main",这是一个特殊的包名,用于标识一个可执行程序。然后,通过 import "fmt" 导入了 "fmt" 包,这个包提供了格式化输入输出的功能。

部分 2 - 变量声明和切片初始化

var s1 []int
s2 := []int{1, 2, 3}

这部分代码声明了两个切片变量。首先,声明了一个名为 s1 的切片,它是一个空切片,没有任何元素。然后,使用短变量声明方式初始化了一个名为 s2 的切片,其中包含三个整数元素。

部分 3 - 输出语句

fmt.Println("最初的切片数据是: ", s1)
fmt.Println("访问的切片第一个元素是: ", s2[0])

这部分代码使用 fmt.Println 函数输出信息。第一条语句输出了 s1 切片的内容,由于 s1 是空切片,所以输出为空。第二条语句输出了 s2 切片的第一个元素,即索引为 0 的元素,这里是数字 1。

部分 4 - 主函数结束

// 主函数结束
}

这是主函数的结束标志。整个程序的执行从 func main() 开始,直到遇到 } 结束。

运行上面代码

$ go run .\00-BasicSliceOperations-01.go
最初的切片数据是:  []
访问的切片第一个元素是:  1

2.3 修改元素

修改切片或数组中的元素与访问元素非常相似。可以使用索引来定位要修改的元素,并将其替换为新的值。

访问元素的基本语法:

// 假设有一个切片或数组 slice
slice[index] = newValue
  • index :要修改的元素的索引
  • newValue :要将元素修改成的新值

将下面代码粘贴到00-BasicSliceOperations-02.go文件中并保存该文件

package main

import "fmt"

func main() {
    s2 := []int{1, 2, 3}
    s2[0] = 88
    fmt.Println("切片修改完的数据是: ", s2)
    fmt.Println("切片修改完第一个元素的结果是: ", s2[0])

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 包声明和导入

package main

import "fmt"

这部分代码声明了包名为 "main",这是一个特殊的包名,用于标识一个可执行程序。然后,通过 import "fmt" 导入了 "fmt" 包,这个包提供了格式化输入输出的功能。

部分 2 - 主函数

func main() {

这是主函数的开始标志,也是程序的入口点。在 Go 语言中,每个可执行程序都必须包含一个 main 函数作为程序的入口。

部分 3 - 切片的创建和修改

s2 := []int{1, 2, 3}
s2[0] = 88

这部分代码首先声明并初始化一个名为 s2 的整型切片,包含了三个元素 1、2 和 3。然后,它将 s2 的第一个元素(索引为 0)修改为 88

部分 4 - 输出语句

fmt.Println("切片修改完的数据是: ", s2)
fmt.Println("切片修改完第一个元素的结果是: ", s2[0])

这部分代码使用 fmt.Println 函数来输出信息。第一条语句输出修改后的切片 s2 的内容,第二条语句输出修改后的切片 s2 的第一个元素,即索引为 0 的元素。

部分 5 - 主函数结束

}

这是主函数的结束标志。整个程序的执行从 func main() 开始,直到遇到 } 结束。

运行上面代码

$ go run .\00-BasicSliceOperations-02.go
切片修改完的数据是:  [88 2 3]
切片修改完第一个元素的结果是:  88

2.4 添加元素

使用内置的append函数可以向切片末尾添加元素,如果底层数组的容量不够,append会自动扩容。

添加元素的基本语法:

// 假设有一个切片 slice
newSlice := append(slice, element1, element2, ...)
  • slice :要添加元素的切片
  • element1element2 等:要添加的元素

将下面代码粘贴到00-BasicSliceOperations-03.go文件中并保存该文件

package main

import "fmt"

func main() {
    s1 := []int{1, 2, 3}
    s1 = append(s1, 4)
    fmt.Println("添加完元素后的结果是: ", s1)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 包声明和导入

package main

import "fmt"

这部分代码声明了包名为 "main",这是一个特殊的包名,用于标识一个可执行程序。然后,通过 import "fmt" 导入了 "fmt" 包,这个包提供了格式化输入输出的功能。

部分 2 - 主函数

func main() {

这是主函数的开始标志,也是程序的入口点。在 Go 语言中,每个可执行程序都必须包含一个 main 函数作为程序的入口。

部分 3 - 切片的创建和添加元素

s1 := []int{1, 2, 3}
s1 = append(s1, 4)

这部分代码首先声明并初始化一个名为 s1 的整型切片,包含了三个元素 1、2 和 3。然后,使用 append 函数将元素 4 添加到切片 s1 的末尾。

部分 4 - 输出语句

fmt.Println("添加完元素后的结果是: ", s1)

这部分代码使用 fmt.Println 函数输出文本和切片的内容。它将添加元素后的切片 s1 的内容打印出来。

部分 5 - 主函数结束

}

这是主函数的结束标志。整个程序的执行从 func main() 开始,直到遇到 } 结束。

运行上面代码

$ go run .\00-BasicSliceOperations-03.go
添加完元素后的结果是:  [1 2 3 4]

2.5 创建切片

在Go语言中,创建切片的方法有多种。最常见的方式是使用make函数,它可以用来创建一个指定长度和容量的切片。

使用 make 函数创建切片的基本语法:

// 声明一个切片
slice := make([]T, length, capacity)
  • T :表示切片中的元素类型
  • length: 表示切片的初始长度
  • capacity :表示切片的初始容量

接下来创建一个整型切片,将下面代码粘贴到00-BasicSliceOperations-04.go文件中并保存该文件

package main

import "fmt"

func main() {
    var s1 []int
    s2 := make([]int, 3, 5)
    fmt.Println("切片默认长度是: ", len(s1))
    fmt.Println("切片默认容量是: ", cap(s1))
    fmt.Println("创建后的切片长度是: ", len(s2))
    fmt.Println("创建后的切片容量是: ", cap(s2))
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 包声明和导入

package main

import "fmt"

这部分代码声明了包名为 "main",这是一个特殊的包名,用于标识一个可执行程序。然后,通过 import "fmt" 导入了 "fmt" 包,这个包提供了格式化输入输出的功能。

部分 2 - 主函数

func main() {

这是主函数的开始标志,也是程序的入口点。在 Go 语言中,每个可执行程序都必须包含一个 main 函数作为程序的入口。

部分 3 - 声明切片和创建切片

var s1 []int
s2 := make([]int, 3, 5)

这部分代码首先声明了一个名为 s1 的空切片。然后,使用 make 函数创建一个名为 s2 的切片,初始长度为 3,容量为 5。

部分 4 - 输出语句

fmt.Println("切片默认长度是: ", len(s1))
fmt.Println("切片默认容量是: ", cap(s1))
fmt.Println("创建后的切片长度是: ", len(s2))
fmt.Println("创建后的切片容量是: ", cap(s2))

这部分代码使用 fmt.Println 函数输出文本和切片的长度和容量信息。它分别打印了切片 s1 的长度和容量(切片为空,所以长度和容量都为 0),以及切片 s2 的长度和容量(根据 make 函数指定的值)。

部分 5 - 主函数结束

}

这是主函数的结束标志。整个程序的执行从 func main() 开始,直到遇到 } 结束。

运行上面代码

$ go run .\00-BasicSliceOperations-04.go
切片默认长度是:  0
切片默认容量是:  0
创建后的切片长度是:  3
创建后的切片容量是:  5

2.6 切片长度和容量

切片具有长度(Length)和容量(Capacity)两个属性。长度是切片中实际包含的元素数量,而容量是底层数组从切片的开始位置到数组末尾的元素数量。

切片长度和容量的基本语法:

// 假设有一个切片 slice
length := len(slice)     // 获取切片的长度
capacity := cap(slice)   // 获取切片的容量
  • slice :要获取长度和容量的切片

将下面代码粘贴到00-BasicSliceOperations-05.go文件中并保存该文件

package main

import "fmt"

func main() {
    s1 := make([]int, 5, 10)
    fmt.Println("切片s1默认长度是: ", len(s1))
    fmt.Println("切片s1默认容量是: ", cap(s1))
    fmt.Println("切片s1初始数据是: ", s1)
    s1 = append(s1, 1, 2, 3)
    fmt.Println("第一次增加后的初始数据是: ", s1)
    fmt.Println("第一次增加后切片s1长度是: ", len(s1))
    fmt.Println("第一次增加后切片s1容量是: ", cap(s1))
    s1 = append(s1, 4, 5, 6)
    fmt.Println("第二次增加后的初始数据是: ", s1)
    fmt.Println("第二次增加后切片s1长度是: ", len(s1))
    fmt.Println("第二次增加后切片s1容量是: ", cap(s1))
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 包声明和导入

package main

这部分代码声明了包名为 "main",这是一个特殊的包名,用于标识一个可执行程序。

部分 2 - 导入语句

import "fmt"

这部分代码通过 import 关键字导入了 "fmt" 包,这个包提供了格式化输入输出的功能。

部分 3 - 主函数

func main() {

这是主函数的开始标志,也是程序的入口点。在 Go 语言中,每个可执行程序都必须包含一个 main 函数作为程序的入口。

部分 4 - 创建切片和输出初始信息

s1 := make([]int, 5, 10)
fmt.Println("切片s1默认长度是: ", len(s1))
fmt.Println("切片s1默认容量是: ", cap(s1))
fmt.Println("切片s1初始数据是: ", s1)

这部分代码首先使用 make 函数创建一个初始长度为 5、容量为 10 的整型切片 s1。然后,通过 fmt.Println 分别打印了切片的长度、容量和初始内容。

部分 5 - 添加元素并输出信息

s1 = append(s1, 1, 2, 3)
fmt.Println("第一次增加后的初始数据是: ", s1)
fmt.Println("第一次增加后切片s1长度是: ", len(s1))
fmt.Println("第一次增加后切片s1容量是: ", cap(s1))

这部分代码使用 append 函数向切片 s1 添加了元素 1、2 和 3。然后,通过 fmt.Println 打印了添加元素后的切片内容、长度和容量。

部分 6 - 再次添加元素并输出信息

s1 = append(s1, 4, 5, 6)
fmt.Println("第二次增加后的初始数据是: ", s1)
fmt.Println("第二次增加后切片s1长度是: ", len(s1))
fmt.Println("第二次增加后切片s1容量是: ", cap(s1))

这部分代码再次使用 append 函数向切片 s1 添加了元素 4、5 和 6。然后,通过 fmt.Println 打印了再次添加元素后的切片内容、长度和容量。

部分 7 - 主函数结束

}

这是主函数的结束标志。整个程序的执行从 func main() 开始,直到遇到 } 结束。

注意:当你通过 append 函数向切片 s1 添加了元素时,由于切片 s1 的容量为 10,当添加的元素超过容量时,底层数组会重新分配一个更大的内存块,然后将现有元素复制到新的内存块中,从而实现切片的扩容。一般容量扩容规律是初始容量*2

运行上面代码

$ go run .\00-BasicSliceOperations-05.go
切片s1默认长度是:  5
切片s1默认容量是:  10
切片s1初始数据是:  [0 0 0 0 0]
第一次增加后的初始数据是:  [0 0 0 0 0 1 2 3]
第一次增加后切片s1长度是:  8
第一次增加后切片s1容量是:  10
第二次增加后的初始数据是:  [0 0 0 0 0 1 2 3 4 5 6]
第二次增加后切片s1长度是:  11
第二次增加后切片s1容量是:  20

2.7 切片截取

当在Go语言中使用切片(slice)时,可以使用切片切取(slice slicing)来获取切片中的一部分元素。切片切取允许创建一个新的切片,其中包含原始切片中指定范围的元素。

切片切取的基本语法:

newSlice := originalSlice[startIndex:endIndex]
  • newSlice :一个新的切片
  • originalSlice:要切取的原始切片
  • startIndex :切取的起始索引(包含)
  • endIndex 是切取的结束索引(不包含)

将下面代码粘贴到00-BasicSliceOperations-06.go文件中并保存该文件

package main

import "fmt"

func main() {
    var s1 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    fmt.Println("最初的数据: ", s1)
    // 获取前4位数据
    fmt.Println("切片s1前四位的数据: ", s1[:4])
    // 获取后4位数据
    fmt.Println("切片s1后四位的数据: ", s1[6:])
    // 获取第3位和第4位数据
    fmt.Println("切片s1第3位和第4位数据: ", s1[2:4])
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 包导入

import "fmt"

这部分代码用于导入名为 fmt 的包,它提供了格式化输入和输出的功能。

部分 2 - 主函数

func main() {
    // ...
}

这是程序的入口点,所有的代码逻辑都将在主函数中执行。

部分 3 - 切片的初始化

var s1 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

部分 4 - 输出初始化数据

fmt.Println("最初的数据: ", s1)

这一行代码使用 fmt.Println 函数将切片 s1 的内容输出到控制台,显示切片中的初始数据。

部分 5 - 切片表达式示例

fmt.Println("切片s1前四位的数据: ", s1[:4])
fmt.Println("切片s1后四位的数据: ", s1[6:])
fmt.Println("切片s1第3位和第4位数据: ", s1[2:4])

这部分代码演示了不同切片表达式的使用:

  • s1[:4]:获取从切片开头到索引3(不包括4)的元素,即前四位元素。
  • s1[6:]:获取从索引6开始到切片末尾的所有元素,即后四位元素。
  • s1[2:4]:获取从索引2到索引3(不包括4)的元素,即第3和第4位元素。

运行上面代码

$ go run .\00-BasicSliceOperations-06.go
最初的数据:  [1 2 3 4 5 6 7 8 9 10]
切片s1前四位的数据:  [1 2 3 4]
切片s1后四位的数据:  [7 8 9 10]
切片s1第3位和第4位数据:  [3 4]

2.8 删除元素

Go语言中没有直接的删除切片元素的方法,但可以通过切片操作来实现删除。

2.8.1 删除第一个元素

将下面代码粘贴到00-BasicSliceOperations-07.go文件中并保存该文件

package main

import "fmt"

func main() {
    var s1 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    fmt.Println("最初的数据: ", s1)
    // 删除第一个元素
    s1 = s1[1:]
    fmt.Println("删除第一个元素: ", s1)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 包导入

import "fmt"

这部分代码用于导入名为 fmt 的包,它提供了格式化输入和输出的功能。

部分 2 - 主函数

func main() {
    // ...
}

这是程序的入口点,所有的代码逻辑都将在主函数中执行。

部分 3 - 初始切片创建

var s1 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

这一行代码创建了一个初始切片 s1,其中包含整数 1 到 10。

部分 4 - 打印初始数据

fmt.Println("最初的数据: ", s1)

这行代码使用 fmt.Println 函数来打印初始切片 s1 中的所有元素,显示在控制台上。

部分 5 - 删除第一个元素

s1 = s1[1:]

在这行代码中,使用了切片的切割操作,通过 s1[1:] 来创建一个新的切片。这个新切片包含原始切片 s1 从索引 1 到最后一个元素的部分,从而达到删除原始切片中第一个元素的效果。

部分 6 - 打印删除后的数据

fmt.Println("删除第一个元素: ", s1)

这行代码使用 fmt.Println 函数来打印删除第一个元素后的切片 s1 中的所有元素,显示在控制台上。

运行上面代码

$ go run .\00-BasicSliceOperations-07.go
最初的数据:  [1 2 3 4 5 6 7 8 9 10]
删除第一个元素:  [2 3 4 5 6 7 8 9 10]

2.8.2 删除最后一个元素

将下面代码粘贴到00-BasicSliceOperations-08.go文件中并保存该文件

package main

import "fmt"

func main() {
    var s1 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    fmt.Println("最初的数据: ", s1)
    // 删除最后一个元素
    s1 = s1[:len(s1)-1]
    fmt.Println("删除最后一个元素: ", s1)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 包导入

import "fmt"

这部分代码用于导入名为 fmt 的包,它提供了格式化输入和输出的功能。

部分 2 - 主函数

func main() {
    // ...
}

这是程序的入口点,所有的代码逻辑都将在主函数中执行。

部分 3 - 初始切片创建

var s1 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

这一行代码创建了一个初始切片 s1,其中包含整数 1 到 10。

部分 4 - 打印初始数据

fmt.Println("最初的数据: ", s1)

这行代码使用 fmt.Println 函数来打印初始切片 s1 中的所有元素,显示在控制台上。

部分 5 - 删除最后一个元素

s1 = s1[:len(s1)-1]

在这行代码中,使用切片的切割操作,通过 s1[:len(s1)-1] 来创建一个新的切片。这个新切片包含原始切片 s1 从索引 0 到倒数第二个元素的部分,即删除了最后一个元素。

部分 6 - 打印删除后的数据

fmt.Println("删除最后一个元素: ", s1)

这行代码使用 fmt.Println 函数将删除最后一个元素后的切片 s1 中的所有元素打印到控制台,以展示删除效果。

运行上面代码

$ go run .\00-BasicSliceOperations-08.go
最初的数据:  [1 2 3 4 5 6 7 8 9 10]
删除最后一个元素:  [1 2 3 4 5 6 7 8 9]

2.8.3 删除某个元素

下面演示如何删除某个元素(这里指5),将下面代码粘贴到00-BasicSliceOperations-09.go文件中并保存该文件

package main

import "fmt"

func main() {
    var s1 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    fmt.Println("最初的数据: ", s1)
    // 删除元素5 [:4] [6:]
    s1 = append(s1[:4], s1[5:]...)
    fmt.Println("删除中间元素5: ", s1)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 包导入

import "fmt"

这部分代码用于导入名为 fmt 的包,它提供了格式化输入和输出的功能。

部分 2 - 初始切片创建

var s1 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

这一行代码创建了一个初始切片 s1,其中包含整数从 1 到 10。

部分 3 - 打印初始数据

fmt.Println("最初的数据: ", s1)

这行代码使用 fmt.Println 函数将初始切片 s1 中的所有元素打印到控制台,用于显示初始数据。

部分 4 - 删除中间元素

s1 = append(s1[:4], s1[5:]...)

在这行代码中,使用切片的切割和重新组合操作来删除切片中的中间元素。s1[:4] 表示切片的前四个元素,s1[5:] 表示切片的从索引 5 开始的元素。通过使用 append 函数将这两部分重新组合在一起,删除了切片中的第 5 个元素(索引为 4 的元素)。

部分 5 - 打印删除后的数据

fmt.Println("删除中间元素5: ", s1)

这行代码使用 fmt.Println 函数将删除中间元素后的切片 s1 中的所有元素打印到控制台,以展示删除效果。

运行上面代码

$ go run .\00-BasicSliceOperations-09.go
最初的数据:  [1 2 3 4 5 6 7 8 9 10]
删除中间元素5:  [1 2 3 4 6 7 8 9 10]

2.9 深拷贝

2.9.1 什么是深拷贝

深拷贝是指在将一个切片赋值给另一个切片时,不仅复制了切片的指针和长度信息,还会创建一个全新的底层数组,并将原切片的元素复制到新的底层数组中。这样,两个切片之间的数据完全独立,修改一个切片不会影响另一个切片。

通俗点来讲,现在想象你有两个完全相同的宝箱,每个都有一样的彩色玻璃珠。你和你的朋友分别拿了一个宝箱,都不知道对方的宝箱。你往自己的宝箱里放玻璃珠,这不会影响到你朋友的宝箱。深拷贝就是这样,两个切片虽然有相同的数据,但它们是独立的,互不干扰。

2.9.2 深拷贝应用场景

如果复制变量涉及到值类型,一般都是深拷贝。其中值类型包括:

  • int
  • float
  • string
  • struct
  • array
  • bool

2.9.3 深拷贝示例

将下面代码粘贴到01-DeepCopy-1.go文件中并保存该文件

package main

import "fmt"

func main() {
    s1 := []int{1, 2, 3, 4, 5}
    s2 := make([]int, len(s1))
    // 深拷贝
    copy(s2, s1)
    // 修改s2中的第一个元素为10
    s2[0] = 10
    fmt.Println("初始数据是: ", s1)
    fmt.Println("修改后的数据是: ", s2)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 包导入

import "fmt"

这行代码导入了Go语言标准库中的fmt包,它提供了格式化输入输出的功能,你可以通过它来打印输出内容。

部分 2 - 主函数

func main() {
   // ...
}

这是Go程序的主函数,程序从这里开始执行。

部分 3 - 切片的创建和赋值

s1 := []int{1, 2, 3, 4, 5}
s2 := make([]int, len(s1))
copy(s2, s1)
  • s1 是一个整数切片,包含了值为 1 到 5 的元素。
  • s2 是通过 make 函数创建的整数切片,其长度与 s1 相同。然后,通过 copy 函数,将 s1 中的元素复制到 s2 中,实现了对 s1 的深拷贝。

部分 4 - 修改切片元素

s2[0] = 10

这行代码将 s2 切片的第一个元素(索引为0)从原来的值修改为10。

部分 5 - 打印输出

fmt.Println(s1)
fmt.Println(s2)

这两行代码使用了导入的 fmt 包中的 Println 函数,分别将 s1s2 切片的内容打印输出到控制台。

运行上面代码

$ go run .\01-DeepCopy-1.go
初始数据是:  [1 2 3 4 5]
修改后的数据是:  [10 2 3 4 5]

2.10 浅拷贝

2.10.1 什么是浅拷贝

浅拷贝是指在将一个切片赋值给另一个切片时,只复制了切片的指针和长度信息,而底层的数据仍然被多个切片所共享。这意味着如果你修改了一个切片的元素,另一个切片也会受到影响,因为它们共享同一份底层数据。

通俗点来讲,你可以想象有一个宝箱里放了一些彩色玻璃珠。你拿了一个空瓶子,然后用一个标签指向宝箱,这样你知道玻璃珠在哪里。这时,你的朋友也来拿了一个空瓶子,也用一个标签指向了同样的宝箱。现在,无论你还是你的朋友往瓶子里放东西,都会影响到同一个宝箱里的东西。这就是浅拷贝,两个切片共享同一个数据,一个变了,另一个也会变。

2.10.2 浅拷贝应用场景

如果复制变量涉及到引用类型,一般都是浅拷贝。其中引用类型包括:

  • slice
  • map
  • channel
  • interface

2.10.3 浅拷贝示例

将下面代码粘贴到02-ShallowCopy-1.go文件中并保存该文件

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    s1 := []int{1, 2, 3, 4, 5}
    // 浅拷贝
    s2 := s1
    s2[0] = 88
    fmt.Println("s1的数据: ", s1)
    fmt.Println("s1中第一个元素的内存地址: ", unsafe.Pointer(&s1[0]))
    fmt.Println("s2的数据: ", s2)
    fmt.Println("s2中第一个元素的内存地址: ", unsafe.Pointer(&s2[0]))
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 包导入

import "fmt"

这部分代码用于导入名为 fmt 的包,它提供了格式化输入和输出的功能。

部分 2 - 初始切片创建

s1 := []int{1, 2, 3, 4, 5}

部分 3 - 浅拷贝

s2 := s1

这行代码进行了浅拷贝,将切片 s1 的内容复制给了切片 s2。这意味着 s1s2 指向相同的底层数组,它们共享同一份数据。

部分 4 - 修改s2

s2[0] = 88

在这行代码中,修改了切片 s2 的第一个元素,将其改为 88。

部分 5 - 打印数据

fmt.Println("s1的数据: ", s1)
fmt.Println("s1中第一个元素的内存地址: ", unsafe.Pointer(&s1[0]))
fmt.Println("s2的数据: ", s2)
fmt.Println("s2中第一个元素的内存地址: ", unsafe.Pointer(&s2[0]))

这几行代码使用 fmt.Println 函数打印了以下内容:

  • 切片 s1 中的数据。
  • 切片 s1 中第一个元素的内存地址。
  • 切片 s2 中的数据。
  • 切片 s2 中第一个元素的内存地址。

运行上面代码,通过比较内存地址,可以看到 s1s2 的第一个元素的内存地址相同,这是因为它们共享了同一个底层数组。

$ go run .\02-ShallowCopy-1.go
s1的数据:  [88 2 3 4 5]
s1中第一个元素的内存地址:  0xc00000e420
s2的数据:  [88 2 3 4 5]
s2中第一个元素的内存地址:  0xc00000e420

三、Go数据结构-映射/字典/对象

主要以下几方面介绍Go语言中字典(对象):

  • 什么是字典(对象)
  • map创建
  • map操作

3.1 什么是字典(对象)

Map 是一种无序的键值对集合,也被称为字典、关联数组或哈希表。在 Go 语言中,map 是一种内置的数据结构,用于存储一组键值对,其中键是唯一的,而值可以重复。Map 在实际应用中非常常见,用于快速查找和存储数据。

注意事项

  • 字典是引用类型:当将字典赋值给另一个变量时,它们将共享相同的底层数据。这意味着修改一个变量的字典会影响到另一个变量的字典。
  • 字典的键类型:字典的键可以是任意可比较的类型,如整数、字符串、结构体(只要它们的字段是可比较的),数组等。切片、函数和包含切片的结构体不能作为字典的键。
  • 并发安全性:默认情况下,字典不是并发安全的,如果多个 Goroutine 同时访问和修改同一个字典,可能会引发竞态条件。如果需要在并发环境中使用字典,可以考虑使用 sync.Map

3.2 map创建

在 Go 语言中,要创建一个 map,可以使用内置的 make 函数来初始化一个空的 map。map 是一种用于存储键值对的数据结构,键是唯一的,而值可以重复。

创建map一般存在三种方法,分别如下:

  • 使用 make 函数创建并初始化 map

  • 使用 map 字面值创建并初始化 map

  • 声明并初始化 map 变量

其中第一种方法最常见,具体如下:

3.2.1 方法一

使用 make 函数来创建一个空的 map,并可以在初始化时指定初始键值对。make 函数的参数是 map,以及可选的容量估计值(通常不需要)。

将下面代码粘贴到00-BasicMapOperations-00.go文件中并保存该文件

package main

import "fmt"

func main() {
    teacherAge := make(map[string]int)
    fmt.Println("字典(对象)初始化的值:", teacherAge)
    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
    teacherAge["张三"] = 88 // 后面的值会覆盖前面的值
    fmt.Println("字典(对象)赋值后的值:", teacherAge)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这行代码导入了标准库中的 "fmt" 包,它提供了输入和输出的功能。

部分 2 - 主函数

func main() {
    // ...
}

main 函数是程序的入口,代码从这里开始执行。

部分 3 - 创建和初始化map

teacherAge := make(map[string]int)

这行代码使用 make 函数创建了一个空的 map,用于存储教师的姓名和年龄信息。键的类型为字符串,值的类型为整数。

部分 4 - 打印初始的空map

fmt.Println("字典(对象)初始化的值:", teacherAge)

这行代码使用 fmt.Println 函数将 "字典(对象)初始化的值:" 和空的 map 打印输出。

部分 5 - 添加键值对到map中

teacherAge["张三"] = 18
teacherAge["李四"] = 19
teacherAge["刘五"] = 20

这几行代码使用键值对方式将教师的姓名作为键,年龄作为值,依次添加到 map 中。

部分 6 - 修改已有的键的值

teacherAge["张三"] = 88 // 后面的值会覆盖前面的值

这行代码将已存在键 "张三" 的值修改为 88。在 map 中,相同的键只能有一个唯一的值。

部分 7 - 打印赋值后的map

fmt.Println("字典(对象)赋值后的值:", teacherAge)

这行代码使用 fmt.Println 函数将 "字典(对象)赋值后的值:" 和修改后的 map 打印输出。

运行上面代码

$ go run .\00-BasicMapOperations-00.go
字典(对象)初始化的值 map[]
字典(对象)赋值后的值 map[刘五:20 张三:88 李四:19]

3.2.2 方法二

使用 map 字面值的方式创建并初始化 map

将下面代码粘贴到00-BasicMapOperations-01.go文件中并保存该文件

package main

import "fmt"

func main() {
    Count := map[string]int{
        "one":   1,
        "two":   2,
        "three": 3,
    }
    fmt.Println(Count)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这行代码导入了标准库中的 "fmt" 包,用于输出结果。

部分 2 - 主函数

func main() {
    // ...
}

main 函数是程序的入口,代码从这里开始执行。

部分 3 - 使用map字面值创建map

Count := map[string]int{
    "one":   1,
    "two":   2,
    "three": 3,
}

这段代码使用 map 字面值的方式创建了一个 map,其中包含了三组键值对。键的类型是字符串,值的类型是整数。

部分 4 - 打印map

fmt.Println(Count)

这行代码使用 fmt.Println 函数将创建的 map 打印输出。

运行上面代码

$ go run .\00-BasicMapOperations-01.go
map[one:1 three:3 two:2]

3.2.3 方法三

在声明 map 变量时同时指定其类型:

将下面代码粘贴到00-BasicMapOperations-02.go文件中并保存该文件

package main

import "fmt"

func main() {
    // 未初始化的map
    var Count map[string]int
    fmt.Println("未初始化的map: ", Count)
    // 未初始化的map不能直接使用,需要初始化
    Count = make(map[string]int) // make函数map类型必须要和var指定的map类型一致
    Count["one"] = 1
    fmt.Println("初始化后的map: ", Count)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这行代码导入了标准库中的 "fmt" 包,用于输出结果。

部分 2 - 主函数

func main() {
    // ...
}

main 函数是程序的入口,代码从这里开始执行。

部分 3 - 声明未初始化的map变量

var Count map[string]int

这行代码声明了一个未初始化的 map 变量 Count,键的类型是字符串,值的类型是整数。

部分 4 - 打印未初始化的map

fmt.Println("未初始化的 map: ", Count)

这行代码使用 fmt.Println 函数将未初始化的 map 打印输出。由于 Count 是未初始化的,其值为 nil

部分 5 - 初始化map

Count = make(map[string]int) // make 函数的 map 类型必须与 var 指定的 map 类型一致

这行代码使用 make 函数初始化了 Count map,以便后续可以使用。这里make 函数的 map 类型必须与 var 指定的 map 类型保持一致

部分 6 - 向map添加键值对

Count["one"] = 1

这行代码将键 "one" 的值设置为 1。

部分 7 - 打印初始化的map

fmt.Println("初始化后的 map: ", Count)

这行代码使用 fmt.Println 函数将初始化后的 map 打印输出,显示键值对的内容。

运行上面代码

$ go run .\00-BasicMapOperations-02.go
未初始化的map:  map[]
初始化后的map:  map[one:1]

3.3 map操作

3.3.1 添加键值对

使用键值对方式向字典中添加数据,键和值之间使用 : 分隔。添加的键必须是字典键的类型,而值必须是字典值的类型。

将下面代码粘贴到01-BasicMapOperations-00.go文件中并保存该文件

package main

import "fmt"

func main() {
    teacherAge := make(map[string]int)
    fmt.Println("字典(对象)初始化的值:", teacherAge)
    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
    // 添加新的键值对
    teacherAge["王六"] = 88
    fmt.Println("字典(对象)添加新的键值对的值:", teacherAge)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

package main

import "fmt"
  • package main:指定当前文件属于名为 "main" 的包,这是一个特殊的包名,用于表示一个可独立执行的程序。
  • import "fmt":导入了 Go 标准库中的 "fmt" 包,该包提供了格式化输入输出的功能

部分 2 - 主函数部分

func main() {
  • func main():这是程序的主函数入口。Go 语言中的每个可执行程序都必须包含一个名为 main 的函数,作为程序的起点

部分 3 - 字典的创建和初始化

    teacherAge := make(map[string]int)
  • teacherAge := make(map[string]int):创建了一个名为 teacherAge 的空字典。使用 make 函数来创建 map,第一个参数是键的类型,这里是字符串类型 string,第二个参数是值的类型,这里是整数类型 int

部分 4 - 打印初始空字典内容

    fmt.Println("字典(对象)初始化的值:", teacherAge)
  • fmt.Println("字典(对象)初始化的值:", teacherAge):使用 fmt.Println 函数打印出初始的空字典内容。此处 teacherAge 是一个空字典,因此打印的内容为 map[]

部分 5 - 向字典中添加键值对

    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
  • 这几行代码将不同老师的名字作为键,对应的年龄作为值,添加到了 teacherAge 字典中。

部分 6 - 向字典中添加新的键值对

    teacherAge["王六"] = 88
  • 这行代码添加了一个新的键值对,将键 "王六" 和值 88 添加到了 teacherAge 字典中

部分 7 - 打印更新后的字典内容

    fmt.Println("字典(对象)添加新的键值对的值:", teacherAge)
  • fmt.Println("字典(对象)添加新的键值对的值:", teacherAge):使用 fmt.Println 函数打印出更新后的字典内容。现在字典中包含了所有的老师及其年龄信息,因此输出的内容为 map[刘五:20 张三:18 李四:19 王六:88]

运行上面代码

$ go run .\01-BasicMapOperations-00.go
字典(对象)初始化的值 map[]
字典(对象)添加新的键值对的值 map[刘五:20 张三:18 李四:19 王六:88]

3.3.2 访问值

通过键访问字典中的值,如果键存在,则返回相应的值;如果键不存在,则返回值类型的零值。

将下面代码粘贴到01-BasicMapOperations-01.go文件中并保存该文件

package main

import "fmt"

func main() {
    teacherAge := make(map[string]int)
    fmt.Println("字典(对象)初始化的值:", teacherAge)
    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
    // 查看刘五对应的值
    value := teacherAge["刘五"]
    fmt.Println("字典(对象)刘五的值:", value)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

package main

import "fmt"
  • package main:指定当前文件属于名为 "main" 的包,这是一个特殊的包名,用于表示一个可独立执行的程序。
  • import "fmt":导入了 Go 标准库中的 "fmt" 包,该包提供了格式化输入输出的功能

部分 2 - 主函数部分

func main() {
  • func main():这是程序的主函数入口。Go 语言中的每个可执行程序都必须包含一个名为 main 的函数,作为程序的起点

部分 3 - 字典的创建和初始化

    teacherAge := make(map[string]int)
  • teacherAge := make(map[string]int):创建了一个名为 teacherAge 的空字典。使用 make 函数来创建 map,第一个参数是键的类型,这里是字符串类型 string,第二个参数是值的类型,这里是整数类型 int

部分 4 - 打印初始空字典内容

    fmt.Println("字典(对象)初始化的值:", teacherAge)
  • fmt.Println("字典(对象)初始化的值:", teacherAge):使用 fmt.Println 函数打印出初始的空字典内容。此处 teacherAge 是一个空字典,因此打印的内容为 map[]

部分 5 - 向字典中添加键值对

    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
  • 这几行代码将不同老师的名字作为键,对应的年龄作为值,添加到了 teacherAge 字典中。

部分 6 - 获取特定键的值并打印

    value := teacherAge["刘五"]
    fmt.Println("字典(对象)刘五的值:", value)
  • 这几行代码演示了如何从字典中获取特定键的值。使用键 "刘五" 获取了对应的值,并将其存储在变量 value 中。然后使用 fmt.Println 函数打印出了键 "刘五" 对应的值。

运行上面代码

$ go run .\01-BasicMapOperations-01.go
字典(对象)初始化的值 map[]
字典(对象)刘五的值 20

3.3.3 修改值

通过键访问字典中的值,然后重新赋值来修改字典中的值。

将下面代码粘贴到01-BasicMapOperations-02.go文件中并保存该文件

package main

import "fmt"

func main() {
    teacherAge := make(map[string]int)
    fmt.Println("字典(对象)初始化的值:", teacherAge)
    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
    // 修改刘五的值为22
    teacherAge["刘五"] = 22
    value := teacherAge["刘五"]
    fmt.Println("字典(对象)刘五修改后的值:", value)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

package main

import "fmt"
  • package main:指定当前文件属于名为 "main" 的包,这是一个特殊的包名,用于表示一个可独立执行的程序。
  • import "fmt":导入了 Go 标准库中的 "fmt" 包,该包提供了格式化输入输出的功能

部分 2 - 主函数部分

func main() {
  • func main():这是程序的主函数入口。Go 语言中的每个可执行程序都必须包含一个名为 main 的函数,作为程序的起点

部分 3 - 字典的创建和初始化

    teacherAge := make(map[string]int)
  • teacherAge := make(map[string]int):创建了一个名为 teacherAge 的空字典。使用 make 函数来创建 map,第一个参数是键的类型,这里是字符串类型 string,第二个参数是值的类型,这里是整数类型 int

部分 4 - 打印初始空字典内容

    fmt.Println("字典(对象)初始化的值:", teacherAge)
  • fmt.Println("字典(对象)初始化的值:", teacherAge):使用 fmt.Println 函数打印出初始的空字典内容。此处 teacherAge 是一个空字典,因此打印的内容为 map[]

部分 5 - 向字典中添加键值对

    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
  • 这几行代码将不同老师的名字作为键,对应的年龄作为值,添加到了 teacherAge 字典中。

部分 6 - 修改键的值

    teacherAge["刘五"] = 22
  • 这行代码将键 "刘五" 对应的值从 20 修改为 22。

部分 7 - 获取修改后的键的值并打印

    value := teacherAge["刘五"]
    fmt.Println("字典(对象)刘五修改后的值:", value)
  • 这几行代码演示了如何从字典中获取特定键的值。使用键 "刘五" 获取了修改后的对应值 22,并将其存储在变量 value 中。然后使用 fmt.Println 函数打印出了键 "刘五" 修改后的值。

运行上面代码

$ go run .\01-BasicMapOperations-02.go
字典(对象)初始化的值 map[]
字典(对象)刘五修改后的值 22

3.3.4 删除键值对

使用 delete 函数来删除字典中的键值对,将要删除的键作为参数传递给 delete 函数。

将下面代码粘贴到01-BasicMapOperations-03.go文件中并保存该文件

package main

import "fmt"

func main() {
    teacherAge := make(map[string]int)
    fmt.Println("字典(对象)初始化的值:", teacherAge)
    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
    // 删除键值对
    delete(teacherAge, "刘五")
    fmt.Println("字典(对象)删除键值对后的值:", teacherAge)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

package main

import "fmt"
  • package main:指定当前文件属于名为 "main" 的包,这是一个特殊的包名,用于表示一个可独立执行的程序。
  • import "fmt":导入了 Go 标准库中的 "fmt" 包,该包提供了格式化输入输出的功能

部分 2 - 主函数部分

func main() {
  • func main():这是程序的主函数入口。Go 语言中的每个可执行程序都必须包含一个名为 main 的函数,作为程序的起点

部分 3 - 字典的创建和初始化

    teacherAge := make(map[string]int)
  • teacherAge := make(map[string]int):创建了一个名为 teacherAge 的空字典。使用 make 函数来创建 map,第一个参数是键的类型,这里是字符串类型 string,第二个参数是值的类型,这里是整数类型 int

部分 4 - 打印初始空字典内容

    fmt.Println("字典(对象)初始化的值:", teacherAge)
  • fmt.Println("字典(对象)初始化的值:", teacherAge):使用 fmt.Println 函数打印出初始的空字典内容。此处 teacherAge 是一个空字典,因此打印的内容为 map[]

部分 5 - 向字典中添加键值对

    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
  • 这几行代码将不同老师的名字作为键,对应的年龄作为值,添加到了 teacherAge 字典中。

部分 6 - 删除键值对

    delete(teacherAge, "刘五")
  • 这行代码使用 delete 函数从字典中删除键 "刘五" 及其对应的值

部分 7 - 打印删除键值对后的字典内容

    fmt.Println("字典(对象)删除键值对后的值:", teacherAge)
  • fmt.Println("字典(对象)删除键值对后的值:", teacherAge):使用 fmt.Println 函数打印出删除键值对后的字典内容。现在字典中不再包含键 "刘五" 及其对应的值。

运行上面代码

$ go run .\01-BasicMapOperations-03.go
字典(对象)初始化的值 map[]
字典(对象)删除键值对后的值 map[张三:18 李四:19]

3.3.5 检查键是否存在

在访问字典时,你可以使用第二个返回值来检查指定的键是否存在。如果存在,第二个返回值为 true,否则为 false

将下面代码粘贴到01-BasicMapOperations-04.go文件中并保存该文件

package main

import "fmt"

func main() {
    teacherAge := make(map[string]int)
    fmt.Println("字典(对象)初始化的值:", teacherAge)
    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
    // 检查名为刘五的键是否存在
    age, people := teacherAge["刘五"]
    if people {
        fmt.Println("能查到名为刘五的键,且它对应的值:", age)
    } else {
        fmt.Println("不能查看名为刘五的键")
    }
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

package main

import "fmt"
  • package main:指定当前文件属于名为 "main" 的包,这是一个特殊的包名,用于表示一个可独立执行的程序。
  • import "fmt":导入了 Go 标准库中的 "fmt" 包,该包提供了格式化输入输出的功能

部分 2 - 主函数部分

func main() {
  • func main():这是程序的主函数入口。Go 语言中的每个可执行程序都必须包含一个名为 main 的函数,作为程序的起点

部分 3 - 字典的创建和初始化

    teacherAge := make(map[string]int)
  • teacherAge := make(map[string]int):创建了一个名为 teacherAge 的空字典。使用 make 函数来创建 map,第一个参数是键的类型,这里是字符串类型 string,第二个参数是值的类型,这里是整数类型 int

部分 4 - 打印初始空字典内容

    fmt.Println("字典(对象)初始化的值:", teacherAge)
  • fmt.Println("字典(对象)初始化的值:", teacherAge):使用 fmt.Println 函数打印出初始的空字典内容。此处 teacherAge 是一个空字典,因此打印的内容为 map[]

部分 5 - 向字典中添加键值对

    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
  • 这几行代码将不同老师的名字作为键,对应的年龄作为值,添加到了 teacherAge 字典中。

部分 6 - 检查键是否存在并获取值

    age, people := teacherAge["刘五"]
    if people {
        fmt.Println("能查到名为刘五的键,且它对应的值:", age)
    } else {
        fmt.Println("不能查看名为刘五的键")
    }
  • 这几行代码演示了如何检查字典中是否存在特定键。使用 teacherAge["刘五"] 来尝试获取键 "刘五" 对应的值,然后使用布尔值 people 来表示是否找到了该键。
  • 通过一个条件语句判断是否找到了键 "刘五",如果找到了,则使用 fmt.Println 函数打印出键 "刘五" 对应的值,否则打印出没有找到键 "刘五" 的信息。

运行上面代码

$ go run .\01-BasicMapOperations-04.go
字典(对象)初始化的值 map[]
能查到名为刘五的键且它对应的值 20

3.3.6 迭代map

使用 range 关键字可以在循环中遍历字典的键值对。在每次迭代中,range 会返回键和对应的值。

将下面代码粘贴到01-BasicMapOperations-05.go文件中并保存该文件

package main

import "fmt"

func main() {
    teacherAge := make(map[string]int)
    fmt.Println("字典(对象)初始化的值:", teacherAge)
    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
    // 循环读取字典
    for k, v := range teacherAge {
        fmt.Printf("老师:%s,年龄:%d\n", k, v)
    }
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

package main

import "fmt"
  • package main:指定当前文件属于名为 "main" 的包,这是一个特殊的包名,用于表示一个可独立执行的程序。
  • import "fmt":导入了 Go 标准库中的 "fmt" 包,该包提供了格式化输入输出的功能

部分 2 - 主函数部分

func main() {
  • func main():这是程序的主函数入口。Go 语言中的每个可执行程序都必须包含一个名为 main 的函数,作为程序的起点

部分 3 - 字典的创建和初始化

    teacherAge := make(map[string]int)
  • teacherAge := make(map[string]int):创建了一个名为 teacherAge 的空字典。使用 make 函数来创建 map,第一个参数是键的类型,这里是字符串类型 string,第二个参数是值的类型,这里是整数类型 int

部分 4 - 打印初始空字典内容

    fmt.Println("字典(对象)初始化的值:", teacherAge)
  • fmt.Println("字典(对象)初始化的值:", teacherAge):使用 fmt.Println 函数打印出初始的空字典内容。此处 teacherAge 是一个空字典,因此打印的内容为 map[]

部分 5 - 向字典中添加键值对

    teacherAge["张三"] = 18
    teacherAge["李四"] = 19
    teacherAge["刘五"] = 20
  • 这几行代码将不同老师的名字作为键,对应的年龄作为值,添加到了 teacherAge 字典中。

部分 6 - 循环遍历字典并打印键值对

    for k, v := range teacherAge {
        fmt.Printf("老师:%s, 年龄:%d\n", k, v)
    }
  • 这部分代码使用 for ... range 循环遍历字典的键值对。
  • 对于每个键值对,循环将键赋值给变量 k,值赋值给变量 v,然后使用 fmt.Printf 函数打印出每位老师的名字和年龄。

运行上面代码

$ go run .\01-BasicMapOperations-05.go
字典(对象)初始化的值 map[]
老师李四,年龄19
老师刘五,年龄20
老师张三,年龄18

3.3.7 注意事项

在进行map操作时,有以下事情需要注意:

  • 尝试访问字典中不存在的键会返回值类型的零值。为了区分实际值和零值,你可以使用上述的检查键是否存在的方法。
  • 修改一个不存在的键会导致添加新的键值对。
  • 使用 delete 函数删除一个不存在的键不会引发错误。
  • 字典是无序的,迭代字典时元素的顺序不保证与添加时的顺序一致。
  • 字典是引用类型,传递字典时实际传递的是指向底层数据的指针。

四、Go数据结构-数据嵌套

主要以下几方面介绍Go语言中数据嵌套:

  • 什么是数据嵌套
  • 数据嵌套示例

4.1 什么是数据嵌套

在Go语言中,数据嵌套通常是通过结构体(Struct)来实现的。结构体是一种自定义的数据类型,可以将不同类型的数据字段组合在一起,形成一个更复杂的数据结构。在结构体中,你可以嵌套其他结构体或内置数据类型,以创建更复杂的数据组织方式。

4.2 数据嵌套类型示例

4.2.1 切片嵌套对象

在 Go 语言中,切片嵌套对象是一种将多个对象组织成切片的数据嵌套方式。这种模式适用于需要表示多个相同类型的对象的集合,例如一组学生、员工或订单。

将下面代码粘贴到00-DataNesting-00.go文件中并保存该文件。创建了一个切片 menu,其中包含了两个字典 order1order2,每个字典表示一天的菜单。然后使用循环遍历 menu 切片,输出每一天的菜单及其对应的菜名和价格。

package main

import "fmt"

func main() {
    order1 := map[string]int{
        "韭菜炒蛋": 13,
        "宫保鸡丁": 15,
    }
    order2 := map[string]int{
        "蛋汤":  11,
        "鸡蛋羹": 15,
    }
    // 切片嵌套对象
    var menu []map[string]int
    menu = append(menu, order1, order2)
    for day, order := range menu {
        fmt.Printf("第%d天的菜单是:\n", day+1)
        for dish, price := range order {
            fmt.Printf("\t菜名:%s,价格:%d\n", dish, price)
        }
    }

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这一部分导入了 Go 语言的标准库中的 "fmt" 包,用于格式化输出

部分 2 - 主函数

func main() {
    // 代码内容
}

这是程序的主函数,程序从这里开始执行。

部分 3 - 定义菜单对象

order1 := map[string]int{
    "韭菜炒蛋": 13,
    "宫保鸡丁": 15,
}

order2 := map[string]int{
    "蛋汤":  11,
    "鸡蛋羹": 15,
}

这里定义了两个字典,order1order2,每个字典表示一天的菜单。菜名作为键,价格作为值。

部分 4 - 创建菜单切片

var menu []map[string]int
menu = append(menu, order1, order2)

这里创建了一个切片 menu,并使用 append 函数将两个字典 order1order2 添加到切片中。

部分 5 - 遍历菜单切片

for day, order := range menu {
    // 循环内部的代码
}

这个循环遍历切片 menu,对于每一天的菜单,它会执行循环内部的代码。

部分 6 - 输出菜单信息

fmt.Printf("第%d天的菜单是:\n", day+1)
for dish, price := range order {
    fmt.Printf("\t菜名:%s,价格:%d\n", dish, price)
}

在循环内部,首先输出一天的菜单标题,然后通过嵌套循环遍历这一天的菜单字典,输出每个菜名和价格。

运行上面代码

$ go run .\00-DataNesting-00.go
第1天的菜单是:
        菜名:韭菜炒蛋,价格:13
        菜名:宫保鸡丁,价格:15
第2天的菜单是:
        菜名:蛋汤,价格:11
        菜名:鸡蛋羹,价格:15

4.2.2 对象嵌套对象

在 Go 语言中,对象(结构体)嵌套对象也是一种常见的数据嵌套方式。这种方式适用于将一个结构体嵌套在另一个结构体中,以构建更复杂的数据模型。

将下面代码粘贴到00-DataNesting-01.go文件中并保存该文件。假设有两个不同的结构体,分别是 Author(作者)和 Book(书籍)。通过对象嵌套对象的方式,将 Author 嵌套在 Book 结构体中,以表示一本书的作者信息。

package main

import "fmt"

type Author struct {
    FirstName string
    LastName  string
}

type Book struct {
    Title  string
    Year   int
    Author Author
}

func main() {
    author := Author{
        FirstName: "z",
        LastName:  "q",
    }

    book := Book{
        Title:  "从零开始学Go",
        Year:   2023,
        Author: author,
    }

    fmt.Println("本书的主题:", book.Title)
    fmt.Printf("本书的主题: %s%s\n", book.Author.FirstName, book.Author.LastName)
    fmt.Println("本书的出版年份: ", book.Year)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这一部分导入了 Go 语言的标准库中的 "fmt" 包,用于格式化输出

部分 2 - 定义结构体

type Author struct {
    FirstName string
    LastName  string
}

type Book struct {
    Title  string
    Year   int
    Author Author
}

这里定义了两个结构体:AuthorBookAuthor 结构体包含了作者的名字和姓氏,Book 结构体表示一本书的信息,包括标题、出版年份和作者。Book 结构体中使用了嵌套结构体 Author,以表示书的作者信息。

部分 3 - 主函数

func main() {
    // 代码内容
}

这是程序的主函数,程序从这里开始执行。

部分 4 - 创建作者对象和书籍对象

author := Author{
    FirstName: "z",
    LastName:  "q",
}

book := Book{
    Title:  "从零开始学Go",
    Year:   2023,
    Author: author,
}

这部分代码创建了一个 Author 对象和一个 Book 对象。author 对象表示作者的信息,book 对象表示书籍的信息。注意,book 对象中的 Author 字段使用了 author 对象作为值,以表示书的作者信息。

部分 5 - 输出书籍信息

fmt.Println("本书的主题:", book.Title)
fmt.Printf("本书的作者: %s%s\n", book.Author.FirstName, book.Author.LastName)
fmt.Println("本书的出版年份: ", book.Year)

这部分代码使用 fmt.Printlnfmt.Printf 函数分别输出了书的标题、作者的名字和姓氏(没有空隙),以及书的出版年份。注意,我们通过 book.Author.FirstNamebook.Author.LastName 来访问嵌套的作者结构体中的字段。

运行上面代码

$ go run .\00-DataNesting-01.go
本书的主题 从零开始学Go
本书的主题: zq
本书的出版年份:  2023

五、Go数据处理-类型转换strconv

主要以下几方面介绍Go语言中类型转换:

  • 什么是strconv
  • 字符串转整数(Atoi)
  • 整数转字符串(Itoa)
  • 字符串转浮点数(ParseFloat)
  • 布尔值转字符串(FormatBool)
  • 字符串转布尔值(ParseBool)

5.1 什么是strconv

strconv 是 Go 语言标准库中的一个包,用于字符串与基本数据类型之间的相互转换。它提供了一系列函数,可以用于将字符串解析为各种基本数据类型,或将基本数据类型格式化为字符串。strconv 包在处理用户输入、配置文件解析等场景中非常有用。

5.2 strconv常用函数

下面介绍一下strconv包中常用函数:

5.2.1 字符串转整数(Atoi)

Atoi 函数的语法如下:

func Atoi(s string) (int, error)

它接受一个字符串参数 s,并返回两个值:一个 int 类型的整数和一个 error 类型的错误。如果转换成功,函数返回转换后的整数和 nil 的错误;如果转换失败,函数返回一个非 nil 的错误,其中错误信息描述了转换失败的原因。

注意:如果输入的字符串无法解析为整数,或者包含了超出整数范围的值,转换将失败,函数会返回一个错误。

下面将字符串"123"转换成整数,将下面代码粘贴到00-TypeConversion-00.go文件中并保存该文件。

package main

import (
    "fmt"
    "strconv"
)

func main() {
    str := "123"
    num, err := strconv.Atoi(str)
    if err != nil {
        fmt.Println("转换失败", err)
    } else {
        fmt.Println("转换成功:", num)
    }
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包和包声明部分

package main

import (
    "fmt"
    "strconv"
)

这部分代码包含了两个重要的部分。首先是包声明,package main 表示这个文件属于名为 "main" 的包,这是 Go 程序的入口包。然后是导入语句,import 用于导入需要在代码中使用的外部包。在这里,fmt 包用于格式化输出,strconv 包用于字符串与数字之间的转换。

部分 2 - 主函数

func main() {
    // ...
}

这是程序的主函数,所有的执行从这里开始。程序在运行时会首先执行 main() 函数内的代码。

部分 3 - 字符串转换为整数部分

str := "123"
num, err := strconv.Atoi(str)
if err != nil {
    fmt.Println("转换失败", err)
} else {
    fmt.Println("转换成功:", num)
}

这部分代码主要涉及将字符串转换为整数的操作:

  • str := "123":定义了一个名为 str 的字符串变量,并将其初始化为 "123"。这是一个需要被转换的字符串。

  • num, err := strconv.Atoi(str):使用 strconv.Atoi() 函数将字符串 str 转换为整数。num 接收转换后的整数值,err 接收可能的错误。Atoi 代表 "ASCII to integer",它将字符串表示的整数转换为整数类型。

  • if err != nil:这是一个条件语句,用于检查是否发生了转换错误。如果 err 不等于 nil,表示发生了错误,执行 if 后的代码块。否则,执行 else 后的代码块。

  • fmt.Println("转换失败", err):如果发生了错误,使用 fmt.Println() 打印出错误信息。err 包含了错误的详细信息。

  • fmt.Println("转换成功:", num):如果没有发生错误,使用 fmt.Println() 打印出转换成功的消息以及转换后的整数值。

运行上面代码

$ go run .\00-TypeConversion-00.go
转换成功 123

5.2.2 整数转字符串(Itoa)

Itoa 函数的语法如下:

func strconv.Itoa(i int)
  • Itoa 函数接受一个整数参数 i,并返回一个对应的字符串表示

下面将整数123转换为字符串"123",将下面代码粘贴到00-TypeConversion-01.go文件中并保存该文件。

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

func main() {
    num := 123
    str := strconv.Itoa(num)
    fmt.Println("验证转换前的数值类型是:", reflect.TypeOf(num))
    fmt.Println("转换后的字符串是:", str)
    fmt.Println("验证转换后的数值类型是:", reflect.TypeOf(str))

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包和包声明部分

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

这部分代码涉及两个主要部分。首先是包声明,package main 表示这个文件属于名为 "main" 的包,这是一个 Go 程序的入口包。然后是导入语句,它导入了三个包:fmt 用于格式化输出,reflect 用于获取变量的类型信息,strconv 用于字符串与基本数据类型之间的转换。

部分 2 - 主函数

func main() {
    // ...
}

这是程序的主函数,所有的执行从这里开始。程序在运行时会首先执行 main() 函数内的代码。

部分 3 - 整数转换为字符串部分

num := 123
str := strconv.Itoa(num)
fmt.Println("验证转换前的数值类型是:", reflect.TypeOf(num))
fmt.Println("转换后的字符串是:", str)
fmt.Println("验证转换后的数值类型是:", reflect.TypeOf(str))

这部分代码演示了将整数转换为字符串并验证类型的过程。

  • num := 123:定义一个整数变量 num 并赋值为 123

  • str := strconv.Itoa(num):使用 strconv.Itoa() 函数将整数 num 转换为字符串,将结果存储在变量 str 中。

  • fmt.Println("验证转换前的数值类型是:", reflect.TypeOf(num)):使用 fmt.Println() 打印出 num 的类型。reflect.TypeOf() 函数返回变量的类型信息,这里应该显示 int

  • fmt.Println("转换后的字符串是:", str):打印出转换后的字符串。

  • fmt.Println("验证转换后的数值类型是:", reflect.TypeOf(str)):打印出转换后的字符串 str 的类型,应该是 string

运行上面代码

$ go run .\00-TypeConversion-01.go
验证转换前的数值类型是: int
转换后的字符串是 123
验证转换后的数值类型是: string

5.2.3 字符串转浮点数(ParseFloat)

ParseFloat 函数的语法如下:

func strconv.ParseFloat(s string, bitSize int) (float64, error)
  • s string:要转换的字符串表示的浮点数。
  • bitSize int:指定浮点数的位大小。通常使用 3264 来表示单精度和双精度浮点数
  • float64:转换后的浮点数值。
  • error:如果转换失败,则返回一个非空的错误

下面将字符串"3.14"转换为浮点数,将下面代码粘贴到00-TypeConversion-02.go文件中并保存该文件。

package main

import (
    "fmt"
    "strconv"
)

func main() {
    str := "3.14"
    num, err := strconv.ParseFloat(str, 64)
    if err != nil {
        fmt.Println("转换失败:", err)
    } else {
        fmt.Println("转换成功:", num)
    }
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包和包声明部分

package main

import (
    "fmt"
    "strconv"
)

这部分代码涉及包的声明和导入。fmt 包用于格式化输出,strconv 包用于字符串与基本数据类型之间的转换。

部分 2 - 主函数

func main() {
    // ...
}

这是程序的主函数,所有的执行从这里开始。程序在运行时会首先执行 main() 函数内的代码。

部分 3 - 字符串转换为浮点数部分

str := "3.14"
num, err := strconv.ParseFloat(str, 64)
if err != nil {
    fmt.Println("转换失败:", err)
} else {
    fmt.Println("转换成功:", num)
}

这部分代码演示了将字符串转换为浮点数的过程:

  • str := "3.14":定义一个字符串 str 并赋值为 "3.14",这是要转换的字符串表示的浮点数。

  • num, err := strconv.ParseFloat(str, 64):使用 strconv.ParseFloat() 函数将字符串 str 转换为浮点数。num 接收转换后的浮点数值,err 接收可能的错误。

  • if err != nil:这是一个条件语句,用于检查是否发生了转换错误。如果 err 不等于 nil,表示发生了错误,执行 if 后的代码块。

  • fmt.Println("转换失败:", err):如果发生了错误,使用 fmt.Println() 打印出错误信息。err 包含了错误的详细信息。

  • fmt.Println("转换成功:", num):如果没有发生错误,使用 fmt.Println() 打印出转换成功的消息以及转换后的浮点数值

运行上面代码

$ go run .\00-TypeConversion-02.go
转换成功 3.14

5.2.4 布尔值转字符串(FormatBool)

FormatBool 函数的语法如下:

func strconv.FormatBool(b bool) string
  • b bool:要转换的布尔值。
  • string:对应布尔值的字符串表示。可能是 "true""false"

下面将布尔值true转换为字符串,将下面代码粘贴到00-TypeConversion-03.go文件中并保存该文件。

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

func main() {
    a := true
    str := strconv.FormatBool(a)
    fmt.Println("验证转换前的数值类型是:", reflect.TypeOf(a))
    fmt.Println("转换后的字符串是:", str)
    fmt.Println("验证转换后的数值类型是:", reflect.TypeOf(str))
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包和包声明部分

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

这部分代码涉及包的声明和导入。fmt 包用于格式化输出,reflect 包用于获取变量的类型信息,strconv 包用于字符串与基本数据类型之间的转换。

部分 2 - 主函数

func main() {
    // ...
}

这是程序的主函数,所有的执行从这里开始。程序在运行时会首先执行 main() 函数内的代码。

部分 3 - 布尔值转换为字符串部分

a := true
str := strconv.FormatBool(a)
fmt.Println("验证转换前的数值类型是:", reflect.TypeOf(a))
fmt.Println("转换后的字符串是:", str)
fmt.Println("验证转换后的数值类型是:", reflect.TypeOf(str))

这部分代码演示了将布尔值转换为字符串的过程:

  • a := true:定义一个布尔变量 a 并赋值为 true

  • str := strconv.FormatBool(a):使用 strconv.FormatBool() 函数将布尔值 a 转换为字符串,将结果存储在变量 str 中。

  • fmt.Println("验证转换前的数值类型是:", reflect.TypeOf(a)):使用 fmt.Println() 打印出 a 的类型。reflect.TypeOf() 函数返回变量的类型信息,这里应该显示 bool

  • fmt.Println("转换后的字符串是:", str):打印出转换后的字符串。

  • fmt.Println("验证转换后的数值类型是:", reflect.TypeOf(str)):打印出转换后的字符串 str 的类型,应该是 string

运行上面代码

$ go run .\00-TypeConversion-03.go
验证转换前的数值类型是: bool
转换后的字符串是 true
验证转换后的数值类型是: string

5.2.5 字符串转布尔值(ParseBool)

ParseBool 函数的语法如下:

func strconv.ParseBool(str string) (bool, error)
  • str string:要解析的字符串。
  • bool, error:返回两个值,一个是解析后的布尔值,另一个是可能的错误。如果字符串解析成功,函数返回转换后的布尔值和 nil 的错误;如果解析失败,函数返回一个非 nil 的错误,其中错误信息描述了解析失败的原因。

下面将字符串"true"转换为布尔值true,将下面代码粘贴到00-TypeConversion-04.go文件中并保存该文件。

package main

import (
    "fmt"
    "strconv"
)

func main() {
    str := "true"
    b, err := strconv.ParseBool(str)
    if err != nil {
        fmt.Println("转换失败:", err)
    } else {
        fmt.Println("转换成功: ", b)
    }
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
    "fmt"
    "strconv"
)

在这个部分,导入了两个包:fmt 用于格式化输出,strconv 用于字符串和基本数据类型之间的转换。

部分 2 - 主函数

func main() {
    // 代码内容
}

这是程序的主函数,程序从这里开始执行。

部分 3 - 执行字符串到布尔值的转换

str := "true"
b, err := strconv.ParseBool(str)

这部分代码将字符串 "true" 转换为布尔值类型。strconv.ParseBool() 函数返回两个值:一个是转换后的布尔值 b,另一个是可能的错误 err。如果转换成功,errnil;如果转换失败,err 包含了错误信息。

部分 4 - 处理转换结果和错误

if err != nil {
    fmt.Println("转换失败:", err)
} else {
    fmt.Println("转换成功: ", b)
}

这部分代码使用条件语句检查 err 是否为 nil,如果不为 nil,说明转换失败,输出错误信息;如果为 nil,说明转换成功,我们输出转换后的布尔值。

运行上面代码

$ go run .\00-TypeConversion-04.go
转换成功:  true

六、Go数据处理-字符串处理

在 Go 语言中,字符串处理是一个非常重要的方面,因为字符串在编程中扮演了关键的角色。字符串处理包括字符串的创建、连接、切割、查找、替换、转换等操作。

主要以下几方面介绍Go语言中字符串处理:

  • 创建字符串
  • 打印单行字符串和多行字符串
  • 打印字符串长度
  • 截取字符串
  • 字符串大小写转换
  • 查看字符串是否包含某个元素
  • 忽略大小写比较字符串
  • 判断某个元素在字符串中出现次数
  • 根据分隔符分割字符串
  • 拼接字符串
  • 判断是否以某个字符开头或结尾
  • 重复字符串
  • 替换字符串
  • 字符串修剪(去除前后的空格)

6.1 创建字符串

在 Go 中,字符串是由一系列字节组成的,用双引号 " 包裹起来的。同时我们也可以使用```来创建原始字符串。关于反引号和双引号的区别如下:

  • 双引号 " " 用于创建普通的字符串,可以包含转义字符
  • 反引号 ``` 用于创建原始字符串,它会保留字符串中的所有字符,包括转义字符和换行符。原始字符串通常用于正则表达式、多行文本等场景。

将下面代码粘贴到01-StringProcessing-00.go文件中并保存该文件

package main

import "fmt"

func main() {
    s1 := "\t\thello\n"
    fmt.Println("双引号字符串输出的值:", s1)
    s2 := `\t\thello\n`
    fmt.Println("反引号字符串输出的值:", s2)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包,该包包含了用于格式化输出的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 主函数

s1 := "\t\thello\n"
fmt.Println("双引号字符串输出的值:", s1)

s2 := `\t\thello\n`
fmt.Println("反引号字符串输出的值:", s2)

这部分代码展示了双引号字符串和反引号字符串的区别。它包含了两个变量声明和初始化,以及使用 fmt.Println() 打印输出的语句。

  • s1 是一个双引号字符串,其中包含了转义字符 \t(制表符)和 \n(换行符)。当这个字符串被输出时,转义字符会被解释为相应的控制字符,从而产生制表和换行的效果。
  • s2 是一个反引号字符串,其中的内容会被原封不动地保留,不会解释转义字符。这意味着输出中会直接显示 \t\thello\n

运行上面代码

$ go run .\01-StringProcessing-00.go
双引号字符串输出的值          hello

反引号字符串输出的值 \t\thello\n

6.2 打印单行字符串和多行字符串

打印单行字符串使用 fmt.Println()fmt.Printf() 函数。打印多行字符串可以使用多行字符串字面量,将多行文本包裹在三个反引号 ``` 之间。

将下面代码粘贴到02-StringProcessing-00.go文件中并保存该文件

package main

import "fmt"

func main() {
    // 打印单行字符串
    singleLineStr := "This is a single-line string."
    // 打印多行字符串
    multiLineStr := `
    This is a
    multi-line
    string.
    `
    fmt.Println("打印出单行字符串: ", singleLineStr)
    fmt.Println("打印出多行字符串: ", multiLineStr)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包,该包包含了用于格式化输出的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 单行字符串示例

singleLineStr := "This is a single-line string."

这部分代码创建了一个单行字符串变量 singleLineStr,其中包含一句话。

部分 4 - 多行字符串示例

multiLineStr := `
 This is a
 multi-line
 string.
 `

这部分代码使用了多行字符串字面量(反引号 ```),在这种形式下,字符串中的所有换行符和空格都会被保留。这样,多行字符串的内容就可以按照格式显示。

部分 5 - 使用fmt.Println()打印输出

fmt.Println("打印出单行字符串: ", singleLineStr)
fmt.Println("打印出多行字符串: ", multiLineStr)

这部分代码使用 fmt.Println() 函数将单行字符串和多行字符串分别输出到控制台。

运行上面代码

$ go run .\02-StringProcessing-00.go
打印出单行字符串:  This is a single-line string.
打印出单行字符串:
        This is a
        multi-line
        string.

6.3 打印字符串长度

使用 len() 函数获取字符串的字节数,每个中文字符占三个字节。

len() 函数是 Go 语言中的一个内建函数,用于返回字符串、切片、数组、映射等数据类型的长度(元素个数)。以下是 len() 函数的基本语法:

length := len(someValue)

上面参数说明:

  • someValue 是一个字符串、切片、数组、映射等数据类型的变量名
  • length 是用来存储其长度的变量名

将下面代码粘贴到03-StringProcessing-00.go文件中并保存该文件

package main

import "fmt"

func main() {
    str1 := "Hello"
    // 打印字符串(纯字母)
    str1length := len(str1)
    fmt.Println("纯字母字符串的长度: ", str1length)
    // 打印字符串(纯中文)
    str2 := "中国"
    str2length := len(str2)
    fmt.Println("纯中文字符串的长度: ", str2length)
    // 打印字符串(字母和中文混合)
    str3 := "Hello,中国"
    str3length := len(str3)
    fmt.Println("字母和中文混合字符串的长度: ", str3length)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包,该包包含了用于格式化输出的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 计算纯字母字符串的长度

str1 := "Hello"
str1length := len(str1)
fmt.Println("纯字母字符串的长度: ", str1length)

这部分代码创建了一个纯字母字符串 str1,并使用 len() 函数计算字符串的字节数(即长度)。由于 str1 中包含5个字母,因此它的长度是5。

部分 4 - 计算纯中文字符串的长度

str2 := "中国"
str2length := len(str2)
fmt.Println("纯中文字符串的长度: ", str2length)

这部分代码创建了一个纯中文字符串 str2,同样使用 len() 函数计算字符串的字节数。因为每个中文字符在UTF-8编码中占3个字节,所以 str2 的长度是6。

部分 5 - 计算字母和中文混合字符串的长度

str3 := "Hello,中国"
str3length := len(str3)
fmt.Println("字母和中文混合字符串的长度: ", str3length)

这部分代码创建了一个字母和中文混合的字符串 str3,同样使用 len() 函数计算字符串的字节数。由于包含5个字母和2个中文字符,所以 str3 的长度是11。

运行上面代码

go run .\03-StringProcessing-00.go
纯字母字符串的长度:  5
纯中文字符串的长度:  6
字母和中文混合字符串的长度:  12

6.4 截取字符串

在 Go 语言中,可以使用切片操作来获取字符串的子串。切片操作使用中括号 [] 来指定开始索引和结束索引,可以用于截取字符串的一部分。以下是获取字符串子串的基本语法:

substring := str[startIndex:endIndex]

上面参数说明:

  • str 是原始字符串
  • startIndex 是子串的起始索引(包含在子串内)
  • endIndex 是子串的结束索引(不包含在子串内)
  • substring 是用来存储截取的子串的变量名

注意,索引是从 0 开始的,且结束索引是不包含在子串内的。因此,substring 包含了从 startIndexendIndex-1 的字符。

将下面代码粘贴到04-StringProcessing-00.go文件中并保存该文件

package main

import "fmt"

func main() {
    str := "Hello,World!"
    substring := str[6:11]
    fmt.Println("截取第7位到第11位的字符串:", substring)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包,该包包含了用于格式化输出的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 截取子串

str := "Hello,World!"
substring := str[6:11]
fmt.Println("截取第7位到第11位的字符串:", substring)

这部分代码创建了一个原始字符串 str,然后使用切片语法 str[6:11] 截取了从第7个字符到第11个字符的子串。注意,切片索引是从0开始计数的。因此,截取的子串是从 Wd,包括 W 但不包括 d

运行上面代码

$ go run .\04-StringProcessing-00.go
截取第7位到第11位的字符串: World

6.5 字符串大小写转换

如果我们想实现字符串大小写,可以通过以下方式进行转换:

  • 使用 strings.ToUpper() 将字符串转换为大写
  • 使用 strings.ToLower() 将字符串转换为小写
  • 使用 strings.ToTitle() 将字符串转换为首字母大写

使用切片操作获取字符串的子串,将下面代码粘贴到05-StringProcessing-00.go文件中并保存该文件

package main

import (
    "fmt"
    "strings"
)

func main() {
    str := "Hello"
    upperStr := strings.ToUpper(str)
    lowerStr := strings.ToLower(str)
    titleStr := strings.ToTitle(str)
    fmt.Println("将所有字符串转换为大写后的值:", upperStr)
    fmt.Println("将所有字符串转换为小写后的值:", lowerStr)
    fmt.Println("将字符串转换为首字母大写后的值:", titleStr)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
   "fmt"
   "strings"
)

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包和 strings 包,前者提供了格式化输入输出的函数,后者提供了操作字符串的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 转换为大写、小写和首字母大写

str := "Hello"
upperStr := strings.ToUpper(str)
lowerStr := strings.ToLower(str)
titleStr := strings.ToTitle(str)
fmt.Println("将所有字符串转换为大写后的值:", upperStr)
fmt.Println("将所有字符串转换为小写后的值:", lowerStr)
fmt.Println("将字符串转换为首字母大写后的值:", titleStr)

这部分代码使用了 strings 包中的函数来进行字符串大小写转换。具体来说:

  • strings.ToUpper(str) 将字符串转换为大写形式。
  • strings.ToLower(str) 将字符串转换为小写形式。
  • strings.ToTitle(str) 将字符串转换为首字母大写形式。

运行上面代码

$ go run .\05-StringProcessing-00.go
将所有字符串转换为大写后的值: HELLO
将所有字符串转换为小写后的值: hello
将字符串转换为首字母大写后的值: HELLO

6.6 查看字符串是否包含某个元素

使用 strings.Contains() 函数检查字符串是否包含指定的子串。strings.Contains() 函数用于检查一个字符串是否包含另一个子串。它返回一个布尔值,表示是否找到了子串。以下是 strings.Contains() 函数的基本语法:

contains := strings.Contains(str, substr)

上面参数说明:

  • str 是待检查的字符串
  • substr 是要查找的子串
  • contains 则是用来存储结果的布尔变量

使用切片操作获取字符串的子串,将下面代码粘贴到06-StringProcessing-00.go文件中并保存该文件

package main

import (
    "fmt"
    "strings"
)

func main() {
    str := "This is a test string."
    // 使用Contains查看testtest的元素(完全匹配)
    contains1 := strings.Contains(str, "testtest")
    fmt.Println("查看字符串中是否包含testtest: ", contains1)
    // 使用Contains查看testtest的元素(任意匹配其中一个元素即可)
    contains2 := strings.ContainsAny(str, "testtest")
    fmt.Println("查看字符串中是否包含testtest中的任意元素: ", contains2)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
   "fmt"
   "strings"
)

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包和 strings 包,前者提供了格式化输入输出的函数,后者提供了操作字符串的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 使用 strings.Contains() 查看字符串是否包含完全匹配的元素

contains1 := strings.Contains(str, "testtest")
fmt.Println("查看字符串中是否包含 testtest: ", contains1)

这部分代码使用了 strings.Contains() 函数来检查字符串 str 是否包含了 "testtest" 这个完全匹配的子串。输出结果会是 false,因为在 str 中并没有出现 "testtest"

部分 4 - 使用 strings.ContainsAny() 查看字符串是否包含任意匹配的元素

contains2 := strings.ContainsAny(str, "testtest")
fmt.Println("查看字符串中是否包含 testtest 中的任意元素: ", contains2)

这部分代码使用了 strings.ContainsAny() 函数来检查字符串 str 是否包含了 "testtest" 中的任意一个字符。输出结果会是 true,因为在 str 中有 "test" 这个子串。

运行上面代码

$ go run .\06-StringProcessing-00.go
查看字符串中是否包含testtest:  false
查看字符串中是否包含testtest中的任意元素:  true

6.7 忽略大小写比较字符串

使用 strings.EqualFold() 函数比较字符串,忽略大小写。strings.EqualFold() 函数用于比较两个字符串是否相等,忽略大小写的差异。它返回一个布尔值,表示两个字符串是否相等。以下是 strings.EqualFold() 函数的基本语法:

equal := strings.EqualFold(str1, str2)

上面参数说明:

  • str1str2 是要比较的两个字符串
  • equal 则是用来存储结果的布尔变量

将下面代码粘贴到07-StringProcessing-00.go文件中并保存该文件

package main

import (
    "fmt"
    "strings"
)

func main() {
    str1 := "Hello,Go!"
    str2 := "hello,Go!"
    equa1 := strings.EqualFold(str1, str2)
    fmt.Println("两个字符串是否相等:", equa1)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
   "fmt"
   "strings"
)

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包和 strings 包,前者提供了格式化输入输出的函数,后者提供了操作字符串的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 使用 strings.EqualFold() 比较字符串是否相等(忽略大小写)

str1 := "Hello,Go!"
str2 := "hello,Go!"
equal := strings.EqualFold(str1, str2)
fmt.Println("两个字符串是否相等:", equal)

这部分代码使用了 strings.EqualFold() 函数来比较两个字符串是否相等,而且不考虑大小写。因为 "Hello,Go!" 和 "hello,Go!" 是相同的,只是大小写不同,所以输出结果会是 true

运行上面代码

$ go run .\07-StringProcessing-00.go
两个字符串是否相等 true

6.8 判断某个元素在字符串中出现次数

strings.Count() 函数用于统计一个字符串中子串出现的次数。它返回一个整数,表示子串在字符串中出现的次数。以下是 strings.Count() 函数的基本语法:

count := strings.Count(str, substr)

上面参数说明:

  • str 是要检查的字符串
  • substr 是要统计的子串
  • count 则是用来存储结果的整数变量

将下面代码粘贴到08-StringProcessing-00.go文件中并保存该文件

package main

import (
    "fmt"
    "strings"
)

func main() {
    str := "banana"
    count := strings.Count(str, "a")
    fmt.Println("a元素在字符串str出现的次数:", count)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
   "fmt"
   "strings"
)

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包和 strings 包,前者提供了格式化输入输出的函数,后者提供了操作字符串的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 使用 strings.Count() 统计元素出现次数

str := "banana"
count := strings.Count(str, "a")
fmt.Println("a元素在字符串str出现的次数:", count)

这部分代码使用了 strings.Count() 函数来统计字符串 str 中字母 "a" 出现的次数。由于 "banana" 中有三个 "a",所以输出结果会是 a元素在字符串str出现的次数: 3

运行上面代码

$ go run .\08-StringProcessing-00.go
a元素在字符串str出现的次数: 3

6.9 根据分隔符分割字符串

strings.Split() 函数用于将一个字符串按照指定的分隔符进行分割,得到一个字符串切片。它返回一个切片,其中包含了被分割后的子串。以下是 strings.Split() 函数的基本语法:

parts := strings.Split(str, sep)

上面参数说明:

  • str 是要分割的原始字符串
  • sep 是用来分隔的字符串
  • parts 则是用来存储分割后的子串切片的变量

这里又分为两种情况:

  • 保留分割符
  • 不保留分割符

下面针对这两种情况分别进行举例说明:

例如,将字符串按逗号分隔为字符串切片(不保留分割符):

将下面代码粘贴到09-StringProcessing-00.go文件中并保存该文件

package main

import (
    "fmt"
    "strings"
)

func main() {
    str := "apple, banana, orange"
    fruits := strings.Split(str, ",")
    fmt.Println("不保留分割符分割后的结果:", fruits)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
   "fmt"
   "strings"
)

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包和 strings 包,前者提供了格式化输入输出的函数,后者提供了操作字符串的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 使用 strings.Split() 函数分割字符串

str := "apple, banana, orange"
fruits := strings.Split(str, ",")
fmt.Println("不保留分割符分割后的结果:", fruits)

这部分代码使用了 strings.Split() 函数来将字符串 str 按照 , 分割成多个子串,存储在名为 fruits 的字符串切片中。输出结果会是一个切片,包含了拆分后的每个子串。

运行上面代码

$ go run .\09-StringProcessing-00.go
不保留分割符分割后的结果: [apple  banana  orange]

同时也可以在保留分割符的情况下,将字符串按逗号分隔为字符串切片:

将下面代码粘贴到09-StringProcessing-01.go文件中并保存该文件

package main

import (
    "fmt"
    "strings"
)

func main() {
    str := "apple, banana, orange"
    fruits := strings.SplitAfter(str, ",")
    fmt.Println("保留分割符分割后的结果:", fruits)
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
   "fmt"
   "strings"
)

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包和 strings 包,前者提供了格式化输入输出的函数,后者提供了操作字符串的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要s入口点,所有的代码都将从这里开始执行。

部分 3 - 使用 strings.SplitAfter() 函数保留分隔符分割字符串

str := "apple, banana, orange"
fruits := strings.SplitAfter(str, ",")
fmt.Println("保留分割符分割后的结果:", fruits)

这部分代码使用了 strings.SplitAfter() 函数来将字符串 str 按照 , 分隔成多个子串,但保留了分隔符。输出结果会是一个切片,包含了拆分后的每个子串,且分隔符 , 也保留在每个子串的末尾。

运行上面代码

$ go run .\09-StringProcessing-01.go
保留分割符分割后的结果: [apple,  banana,  orange]

6.10 拼接字符串

在Go语言中,字符串运算通常涉及字符串的拼接、截取、查找、替换等操作。

字符串拼接是将多个字符串连接成一个新的字符串。可以使用 + 运算符来进行字符串拼接。

将下面代码粘贴到10-StringProcessing-00.go文件中并保存该文件

package main

import (
    "fmt"
    "reflect"
)

// 字符串运算-字符串拼接
func stringOperation(a, b string) {
    fmt.Printf("a和b拼接的结果:%s\n", a+b)
    ab := a + b
    fmt.Printf("ab: %s,类型是:%s", ab, reflect.TypeOf(ab))
}

func main() {
    stringOperation("1", "2")
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 包的导入

import (
    "fmt"
    "reflect"
)

导入了两个包:

  • fmt 包用于格式化输出
  • reflect 包用于在运行时获取变量的类型信息

部分 2 - stringOperation 函数定义

func stringOperation(a, b string) {
    fmt.Printf("a和b拼接的结果:%s\n", a+b)
    ab := a + b
    fmt.Printf("ab: %s,类型是:%s", ab, reflect.TypeOf(ab))
}

这部分定义了一个名为 stringOperation 的函数。函数接受两个参数 ab,它们都是字符串类型。函数的主要目的是将两个输入字符串拼接在一起,然后输出拼接结果和拼接后字符串的类型。

在函数体内,首先使用 fmt.Printf 打印拼接后的字符串,并使用占位符 %s 将结果格式化为字符串输出。然后使用 a + b 将两个输入字符串拼接成新的字符串 ab。接着使用 reflect.TypeOf 获取 ab 的类型,然后再次使用 fmt.Printf 打印出 ab 的值和类型。

部分 3 - main 函数定义

func main() {
    stringOperation("1", "2")
}

这部分定义了 main 函数,是程序的入口点。在 main 函数中,调用了之前定义的 stringOperation 函数,并传递了两个字符串参数 "1" 和 "2"。这样,stringOperation 函数将被执行,输出拼接结果和拼接后字符串的类型。

运行上面代码

$ go run .\10-StringProcessing-00.go
a和b拼接的结果 12
ab: 12,类型是string

当然除了使用 + 运算符来进行字符串拼接,还可以通过fmt.Sprintf来实现字符串拼接。

将下面代码粘贴到10-StringProcessing-01.go文件中并保存该文件

package main

import (
    "fmt"
)

// fmt.Sprintf实现字符串拼接
func stringSprintf(firstName, secondName string) {
    fullName := fmt.Sprintf("%s%s", secondName, firstName)
    fmt.Println("你的全名是:", fullName)
}

func main() {
    stringSprintf("q", "z")
}

针对上面代码分为几个部分进行详细说明:

部分 1 - 包的导入

import (
    "fmt"
)

这部分代码用于导入所需的包,导入了 fmt 包,这个包提供了格式化的输入和输出功能

部分 2 - stringOperation 函数定义

func stringSprintf(firstName, secondName string) {
    fullName := fmt.Sprintf("%s%s", secondName, firstName)
    fmt.Println("你的全名是:", fullName)
}

这个部分定义了一个名为 stringSprintf 的函数。这个函数接受两个字符串参数,firstNamesecondName,这些参数代表名字的不同部分。函数使用 fmt.Sprintf 函数将这两个名字部分拼接在一起,而不添加额外的空隙。拼接的结果被赋值给变量 fullName。最后,使用 fmt.Println 打印输出全名。

部分 3 - main 函数定义

func main() {
    stringSprintf("q", "z")
}

这部分定义了 main 函数,是整个程序的入口点。在 main 函数中,调用了 stringSprintf 函数,传递了 "q" 和 "z" 作为参数。这样,stringSprintf 函数将会被执行,输出格式化后的全名 "qz"。

运行上面代码

$ go run .\10-StringProcessing-01.go
你的全名是 zq

6.11判断是否以某个字符开头或结尾

6.11.1 判断字符串是否以指定前缀开头(区分大小写)

strings.HasPrefix() 函数用于检查一个字符串是否以指定的前缀开头。它返回一个布尔值,表示字符串是否以指定的前缀开头。以下是 strings.HasPrefix() 函数的基本语法:

startsWith := strings.HasPrefix(str, prefix)

上面参数说明:

  • str 是待检查的字符串
  • prefix 是要检查的前缀
  • startsWith 是用来存储结果的布尔变量

说明:如果字符串 str 以前缀 prefix 开头,那么 startsWith 将会是 true,否则将会是 false

将下面代码粘贴到11-StringProcessing-00.go文件中并保存该文件

package main

import (
    "fmt"
    "strings"
)

func main() {
    str := "Hello"
    startsWith := strings.HasPrefix(str, "He")
    fmt.Println("该字符串是否以He开头的:", startsWith)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
   "fmt"
   "strings"
)

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包和 strings 包,前者提供了格式化输入输出的函数,后者提供了操作字符串的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分3 - 使用 strings.HasPrefix() 函数判断字符串是否以指定前缀开头

str := "Hello"
startsWith := strings.HasPrefix(str, "He")
fmt.Println("该字符串是否以 He 开头的:", startsWith)

这部分代码使用了 strings.HasPrefix() 函数来判断字符串 str 是否以 "He" 这个前缀开头。由于 "Hello" 确实以 "He" 开头,所以输出结果会是 该字符串是否以 He 开头的: true

运行上面代码

$ go run .\11-StringProcessing-00.go
该字符串是否以He开头的 true

6.11.2 判断字符串是否以指定后缀结尾(区分大小写)

strings.HasSuffix() 函数用于检查一个字符串是否以指定的后缀结尾。它返回一个布尔值,表示字符串是否以指定后缀结尾。以下是 strings.HasSuffix() 函数的基本语法:

hasSuffix := strings.HasSuffix(str, suffix)

上面参数说明:

  • str 是待检查的字符串
  • suffix 是要检查的后缀
  • hasSuffix 是用来存储结果的布尔变量

说明:如果字符串 str 以后缀 suffix 结尾,那么 hasSuffix 将会是 true,否则将会是 false

将下面代码粘贴到11-StringProcessing-01.go文件中并保存该文件

package main

import (
    "fmt"
    "strings"
)

func main() {
    str := "Hello"
    startsWith := strings.HasSuffix(str, "lo")
    fmt.Println("该字符串是否以lo结尾的:", startsWith)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
   "fmt"
   "strings"
)

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包和 strings 包,前者提供了格式化输入输出的函数,后者提供了操作字符串的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 使用 strings.HasSuffix() 函数判断字符串是否以指定后缀结尾

str := "Hello"
endsWith := strings.HasSuffix(str, "lo")
fmt.Println("该字符串是否以lo结尾的:", endsWith)

这部分代码使用了 strings.HasSuffix() 函数来判断字符串 str 是否以 "lo" 这个后缀结尾。由于 "Hello" 确实以 "lo" 结尾,所以输出结果会是 该字符串是否以lo结尾的: true

运行上面代码

$ go run .\11-StringProcessing-01.go
该字符串是否以lo结尾的 true

6.12 重复字符串

strings.Repeat() 函数用于将一个字符串重复多次,生成一个新的重复字符串。它接受一个字符串和一个重复的次数作为参数,返回生成的重复字符串。以下是 strings.Repeat() 函数的基本语法:

repeatedStr := strings.Repeat(str, count)

上面参数说明:

  • str 是要重复的字符串
  • count 是重复的次数
  • repeatedStr 是用来存储生成的重复字符串的变量

将下面代码粘贴到12-StringProcessing-00.go文件中并保存该文件

package main

import (
    "fmt"
    "strings"
)

func main() {
    str := "go"
    repeatedStr := strings.Repeat(str, 3)
    fmt.Println("重复生成的字符串:", repeatedStr)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
   "fmt"
   "strings"
)

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包和 strings 包,前者提供了格式化输入输出的函数,后者提供了操作字符串的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 使用 strings.Repeat() 函数生成重复多次的字符串

str := "go"
repeatedStr := strings.Repeat(str, 3)
fmt.Println("重复生成的字符串:", repeatedStr)

这部分代码使用了 strings.Repeat() 函数来将字符串 "go" 重复生成 3 次,生成的结果保存在 repeatedStr 中。输出结果会是 重复生成的字符串: gogogo

运行上面代码

$ go run .\12-StringProcessing-00.go
重复生成的字符串 gogogo

6.13 替换字符串

strings.Replace() 函数用于在一个字符串中替换指定的子串为新的子串。它返回一个新的字符串,其中完成了替换操作。以下是 strings.Replace() 函数的基本语法:

newStr := strings.Replace(str, oldSubstr, newSubstr, n)

上面参数说明:

  • str 是原始字符串
  • oldSubstr 是要被替换的旧子串
  • newSubstr 是要替换为的新子串
  • n 是替换的次数(可以是一个正整数或者 -1)
  • newStr 是用来存储替换后的新字符串的变量

注意:strings.Replace() 函数会在字符串 str 中查找所有的 oldSubstr 并将其替换为 newSubstr。如果指定了参数 n,则最多只会替换前 n 个匹配的子串

将下面代码粘贴到13-StringProcessing-00.go文件中并保存该文件

package main

import (
    "fmt"
    "strings"
)

func main() {
    str := "Hello world!"
    newStr := strings.Replace(str, "world", "go", 1)
    fmt.Println("替换后的字符串:", newStr)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
   "fmt"
   "strings"
)

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包和 strings 包,前者提供了格式化输入输出的函数,后者提供了操作字符串的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 使用 strings.Replace() 函数替换子串

str := "Hello world!"
newStr := strings.Replace(str, "world", "go", 1)
fmt.Println("替换后的字符串:", newStr)

这部分代码使用了 strings.Replace() 函数来在字符串 str 中将第一个出现的子串 "world" 替换为 "go"。因此,输出结果会是 替换后的字符串: Hello go!

运行上面代码

$ go run .\13-StringProcessing-00.go
替换后的字符串 Hello go!

6.14 字符串修剪(去除前后的空格)

strings.TrimSpace() 函数用于去除字符串开头和结尾的空白字符(包括空格、制表符和换行符等)。它返回一个新的字符串,其中已经去除了开头和结尾的空白字符。以下是 strings.TrimSpace() 函数的基本语法:

trimmedStr := strings.TrimSpace(str)

上面参数说明:

  • str 是待修剪的字符串
  • trimmedStr 是用来存储修剪后的新字符串的变量

说明:strings.TrimSpace() 函数会移除字符串 str 开头和结尾的所有空白字符,并返回一个新的字符串。

将下面代码粘贴到14-StringProcessing-00.go文件中并保存该文件

package main

import (
    "fmt"
    "strings"
)

func main() {
    str := "  abc   "
    trimmedStr := strings.TrimSpace(str)
    fmt.Println("修剪后的字符串:", trimmedStr)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import (
   "fmt"
   "strings"
)

这部分代码使用 import 关键字导入了 Go 标准库中的 fmt 包和 strings 包,前者提供了格式化输入输出的函数,后者提供了操作字符串的函数。

部分 2 - 主函数

func main() {
   // ...
}

这是程序的主要入口点,所有的代码都将从这里开始执行。

部分 3 - 使用 strings.TrimSpace() 函数修剪字符串两端的空白字符

str := "  abc   "
trimmedStr := strings.TrimSpace(str)
fmt.Println("修剪后的字符串:", trimmedStr)

这部分代码使用了 strings.TrimSpace() 函数来将字符串 str 两端的空白字符进行修剪。由于 str 开头有两个空格,结尾有三个空格,所以输出结果会是 修剪后的字符串: abc

运行上面代码

$ go run .\14-StringProcessing-00.go
修剪后的字符串: abc

七、Go数据处理-内存地址和指针

在 Go 语言中,字符串处理是一个非常重要的方面,因为字符串在编程中扮演了关键的角色。字符串处理包括字符串的创建、连接、切割、查找、替换、转换等操作。

主要以下几方面介绍Go语言中内存地址和指针:

  • 什么是内存地址和指针
  • 定义内存地址和指针
  • 通过指针获取内存地址的值
  • 通过内存地址直接修改赋值

7.1 什么是内存地址和指针

7.1.1 什么是内存地址

在声明变量时,会在计算机内存中申请一个位置,用于存储、修改和获取变量的值,这个位置被称为内存地址,内存地址使用十六进制表示。这里我们可以把内存地址比喻成我们的"身份证号",是唯一的。

7.1.2 什么是指针

这个内存地址赋值给另外一个变量,这个变量就叫做指针。指针也是一种类型,只不过指针存储的是一个内存地址。这里我们可以把指针比喻成我们的"身份证",用于存储"身份证号",即内存地址。

关于指针有一些注意事项:

  • 指针的初始值:Go 语言中的指针在没有显式赋值之前,会拥有一个默认值 nil,表示它没有指向任何有效的内存地址。
  • 空指针检查:在访问指针指向的值之前,最好先检查指针是否为 nil,以避免空指针引发的异常。
  • 指针的生命周期:如果指针指向的变量不再被引用,它可能会被垃圾回收机制回收,释放相关的内存。
  • 参数传递:当你将一个变量作为参数传递给函数时,实际上是将该变量的一个副本传递给函数。如果你想在函数内部修改原始变量,可以传递指向该变量的指针。

7.2 定义内存地址和指针

上面已经详细介绍过了内存地址和指针,再简单介绍一下:

  • 内存地址是一个标识,用于指示存储单元(字节)在计算机内存中的位置。在Go中,我们使用&操作符来获取变量的内存地址。
  • 指针是一个变量,它存储另一个变量的内存地址。在Go中,我们使用*操作符来声明指针变量。

下面进行举例说明:

将下面代码粘贴到00-MemoryAddressAndPointer-00.go文件中并保存该文件

package main

import "fmt"

func main() {
    var s string
    s = "这是一个字符串"
    // 使用&操作符来获取变量的内存地址
    fmt.Println("变量s的内存地址是: ", &s)
    // 使用语法糖获取指针
    sp := &s
    fmt.Println("指针sp: ", sp)
    // 使用*操作符直接声明指针变量
    var sp2 *string
    fmt.Println("未赋值的指针sp2: ", sp2) //指针未赋值会返回nil
    // 给指针进行赋值
    sp2 = &s
    fmt.Println("已赋值的指针sp2: ", sp2)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这部分导入了名为 "fmt" 的包,这个包提供了格式化输入和输出的功能。

部分 2 - 主函数

func main() {
    // 代码在这里
}

这部分定义了程序的主函数 main(),在这个函数中,你的代码将会被执行。

部分 3 - 声明字符串变量和赋值

var s string
s = "这是一个字符串"

这部分声明了一个名为 s 的字符串变量,并将字符串 "这是一个字符串" 赋值给它。

部分 4 - 获取内存地址

fmt.Println("变量s的内存地址是: ", &s)

这部分使用 & 操作符获取字符串变量 s 的内存地址,并使用 fmt.Println() 函数将地址打印出来。

部分 5 - 使用语法糖获取指针

sp := &s

这部分使用语法糖,将字符串变量 s 的地址赋值给名为 sp 的指针变量。

部分 6 - 打印指针值

fmt.Println("指针sp: ", sp)

这部分使用 fmt.Println() 函数打印指针变量 sp 的值,即字符串变量 s 的地址。

部分 7 - 声明并打印未赋值的指针

var sp2 *string
fmt.Println("未赋值的指针sp2: ", sp2) // 指针未赋值会返回nil

这部分声明了一个名为 sp2 的字符串指针变量,并使用 fmt.Println() 函数打印未赋值的指针变量 sp2。在这里,由于指针未赋值,它的值为 nil

部分 8 - 赋值指针并打印

sp2 = &s
fmt.Println("已赋值的指针sp2: ", sp2)

这部分将字符串变量 s 的地址赋值给指针变量 sp2,然后使用 fmt.Println() 函数打印已赋值的指针变量 sp2

运行上面代码

$ go run .\00-MemoryAddressAndPointer-00.go
变量s的内存地址是:  0xc000056270
指针sp:  0xc000056270
未赋值的指针sp2:  <nil>
已赋值的指针sp2:  0xc000056270

7.3 通过指针获取内存地址的值

在Go中,使用指针来获取内存地址的值需要使用*操作符。这被称为“解引用”。下面进行举例说明:

将下面代码粘贴到01-MemoryAddressAndPointer-00.go文件中并保存该文件

package main

import "fmt"

func main() {
    x := 11
    // 获取变量x的值
    ptr := &x
    // 通过指针获取内存地址的值
    value := *ptr
    fmt.Println("通过指针获取内存地址的值: ", value)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这部分导入了名为 "fmt" 的包,这个包提供了格式化输入和输出的功能。

部分 2 - 主函数

func main() {
    // 代码在这里
}

这部分定义了程序的主函数 main(),在这个函数中,你的代码将会被执行。

部分 3 - 声明并初始化整数变量

x := 11

这部分声明了一个名为 x 的整数变量,并将它初始化为 11

部分 4 - 获取变量的内存地址并赋值给指针

ptr := &x

这部分使用 & 操作符获取整数变量 x 的内存地址,并将这个地址赋值给指针变量 ptr

部分 5 - 通过指针获取内存地址的值

value := *ptr

这部分使用 * 操作符解引用指针 ptr,从指针所指向的内存地址中获取值,并将这个值赋值给变量 value

部分 6 - 打印获取的值

fmt.Println("通过指针获取内存地址的值: ", value)

这部分使用 fmt.Println() 函数来打印通过指针获取的内存地址的值,即变量 x 的值。

运行上面代码

$ go run .\01-MemoryAddressAndPointer-00.go
通过指针获取内存地址的值:  11

7.4 通过内存地址直接修改赋值

我们可以通过指针来访问并修改存储在特定内存地址中的值,这称为“间接赋值”。下面进行举例说明:

将下面代码粘贴到02-MemoryAddressAndPointer-00.go文件中并保存该文件

package main

import "fmt"

func main() {
    x := 11
    // 获取内存地址
    ptr := &x
    fmt.Println("修改之前的内存地址是:", x)

    // 通过指针修改内存地址的值
    *ptr = 100
    fmt.Println("修改之前的内存地址是:", x)

}

针对上面代码分为几个部分进行详细说明:

部分 1 - 导入包

import "fmt"

这部分导入了名为 "fmt" 的包,这个包提供了格式化输入和输出的功能。

部分 2 - 主函数

func main() {
    // 代码在这里
}

这部分定义了程序的主函数 main(),在这个函数中,你的代码将会被执行。

部分 3 - 声明并初始化整数变量

x := 11

这部分声明了一个名为 x 的整数变量,并将它初始化为 11

部分 4 - 获取内存地址并打印

ptr := &x
fmt.Println("修改之前的内存地址是:", x)

这部分使用 & 操作符获取整数变量 x 的内存地址,并将这个地址赋值给指针变量 ptr。然后,使用 fmt.Println() 函数将变量 x 的值打印出来,显示“修改之前的内存地址是:11”。

部分 5 - 通过指针修改内存地址的值

*ptr = 100

这部分使用解引用操作 *ptr 来修改指针 ptr 所指向的内存地址的值。在这里,指针 ptr 指向变量 x 的内存地址,所以通过修改 *ptr,你实际上修改了变量 x 的值,将其从 11 修改为 100

部分 6 - 打印修改后的值

fmt.Println("修改之后的内存地址是:", x)

这部分使用 fmt.Println() 函数将修改后的变量 x 的值打印出来,显示“修改之后的内存地址是:100”。

运行上面代码

$ go run .\02-MemoryAddressAndPointer-00.go
修改之前的内存地址是 11
修改之前的内存地址是 100