一、字符串
字符串的定义
在 Go 语言中,使用双引号""包裹或者使用反引号 ` ` 包裹的数据类型为字符串,反引号可以定义含有多行的字符串。
func main() {
var tango string
tango = "tony"
fmt.Println(tango)
oscar := `
窗前明月光
疑是地上霜`
fmt.Println(oscar)
}
执行上述代码,输出结果如下:
tony
窗前明月光
疑是地上霜
Go 语言中的字符有两种,分别是 byte 类型,表示 ASCII 码的一个字符; rune 类型,代表一个 UTF-8 字符,一个 UTF-8 字符由多个 byte 组成,字符使用单引号表示。
字符串底层起始就是一个 byte 数组,因此 byte 数组和字符串之间是可以相互转换的,字符串的长度就是 byte 数组的长度,因为数组的长度是固定的,因此字符串是不可修改的。
func main() {
// 字符串和字节数组的相互转换
var tango string
tango = "朱瞻基"
tangoBytes := []byte(tango)
fmt.Println("字符串转换成 byte 数组为:", tangoBytes)
tangoStr := string(tangoBytes)
fmt.Println("字节数组转换成字符串为:", tangoStr)
quebec := "This is Quebec"
quebecRune := []rune(quebec)
fmt.Println("字符串转换为字符数组为:", quebecRune)
quebecStr := string(quebec)
fmt.Println("字符数组转换为字符串:", quebecStr)
// 遍历输出字符数组中的每一个字符,以逗号隔开每个字符,空格也算字符
for i := 0; i < len(quebecRune); i++ {
if i < len(quebecRune)-1 {
fmt.Print(string(quebecRune[i]), ",")
} else {
fmt.Println(string(quebecRune[i]))
}
}
}
执行上述代码,输出结果如下:
字符串转换成 byte 数组为: [230 156 177 231 158 187 229 159 186]
字节数组转换成字符串为: 朱瞻基
字符串转换为字符数组为: [84 104 105 115 32 105 115 32 81 117 101 98 101 99]
字符数组转换为字符串: This is Quebec
T,h,i,s, ,i,s, ,Q,u,e,b,e,c
字符串的常见操作
在上面的代码中已经实现了字符串与字节数组和字符数组之间的相互转换,使用 string() 函数可以实现将字节或者字符数组转换为字符串,还有 len() 函数可以获取字符串的长度,除此之外还有其他一些字符串常见的操作:
| 函数 | 说明 | 返回值 |
|---|---|---|
| strings.Sprintf | 拼接字符串 | 字符串 |
| strings.Split | 分割字符串 | 字符串数组 |
| strings.Contains | 判断是否包含指定的字符或者字符串 | 布尔值 |
| strings.HasPrefix | 是否以指定字符或者字符串开头 | 布尔值 |
| strings.HasSuffix | 是否以指定字符或者字符串结尾 | 布尔值 |
| strings.Index | 指定字符第一次出现的位置 | 整数 |
| strings.LastIndex | 指定字符最后一次出现的位置 | 整数 |
| strings.Join | 将字符数组中的元素使用指定的字符连接成字符串 | 字符串 |
这里的 strings 值得是一个模块,既调用 strings 模块中的这些函数来操作字符串,这里的函数都是以大写字母开头,表示是可以公共函数,可以被调用。
func main() {
tango := "This is Tango5."
// 字符串之间使用 + 拼接
fmt.Println(tango + "Do you COPY?")
// 字符串分割
tangoStr := strings.Split(tango, "i")
// 遍历分割出的每一个字符
for i := 0; i < len(tangoStr); i++ {
fmt.Println(tangoStr[i])
}
}
执行上述代码,输出结果如下:
This is Tango5.Do you COPY?
Th
s
s Tango5.
分割字符串时要指定分割字符,分割之后的子字符串会保存在字符串数组中
func main() {
tango := "This is Tango5."
// 判断是否包含字符后者字符串
isContain := strings.Contains(tango, "T")
fmt.Println("是否包含字符 T:", isContain)
isContain = strings.Contains(tango, "This")
fmt.Println("是否包含字符 This:", isContain)
// 判断是否以指定的字符或者字符串开头或结果结尾
startWith := strings.HasPrefix(tango, "T")
fmt.Println("是否以 T 开头:", startWith)
startWith = strings.HasPrefix(tango, "This")
fmt.Println("是否以 This 开头:", startWith)
endWith := strings.HasSuffix(tango, ".")
fmt.Println("是否以 . 结尾:", endWith)
endWith = strings.HasSuffix(tango, "This")
fmt.Println("是否以 This 结尾:", endWith)
}
执行上述代码,输出结果如下:
是否包含字符 T: true
是否包含字符 This: true
是否以 T 开头: true
是否以 This 开头: true
是否以 . 结尾: true
是否以 This 结尾: false
这几个函数的返回值都是布尔值,既 true 或者 false。
func main() {
tango := "This is Tango5."
// 返回指定字符或者子字符串的索引
fmt.Println(strings.Index(tango, "is"))
fmt.Println(strings.Index(tango, "."))
// 返回指定字符或者字符串最后一次出现位置的索引
fmt.Println(strings.LastIndex(tango, "is"))
// 字符数组
var yankee = []string{"I", "am"}
// 使用空格将字符数组连接成字符串
yankeeStr := strings.Join(yankee, " ")
fmt.Printf("yankee 的值为:%v, yankee 的类型为:%T", yankeeStr, yankeeStr)
}
执行上述代码,输出结果如下:
2
14
5
yankee 的值为:I am, yankee 的类型为:string
更多关于字符串的操作可以查看源码 strings 包下的代码
字符串的遍历
字符串的遍历有两种方式,第一种方式就是上述代码中使用的方式,既通过 for + 索引 的方式来遍历字符串。
除此之外可以可以通过 for + range 函数 的方式来遍历字符串
func main() {
tango := "This is Tango5."
for idx, item := range tango {
fmt.Println(idx, "-", item)
}
}
执行上述代码,输出结果如下:
0 - 84
1 - 104
2 - 105
3 - 115
4 - 32
5 - 105
6 - 115
7 - 32
8 - 84
9 - 97
10 - 110
11 - 103
12 - 111
13 - 53
14 - 46
这里输出的是每一个字符的 ASCII 值,要通过 string() 函数转换成字符
func main() {
tango := "This is Tango5."
for _, item := range tango {
fmt.Println(string(item))
}
}
执行上述代码,输出结果如下:
T
h
i
s
i
s
T
a
n
g
o
5
.
需要注意的是 range 函数会返回两个值,一个值是索引,一个值是索引位置对应的元素,所以第一次遍历时使用了 idx 和 item 两个变量来接收索引和元素
第二次遍历时使用了 _ 和 item 来接收索引和元素, _ 称为匿名变量,在 Go 中非匿名变量定义之后一定要使用,否则编译会报错,而匿名变量定义后无须使用,编译不会报错。
字符串与整数、浮点数和布尔值之间的转换
strconv 包中提供了一些列字符串与基本数据类型之间的转换的函数,如 FormatInt,FormatFloat,FormatComplex,FormatBool 等
整数转换成字符串
整数转换成字符串可以使用 FormatInt 函数和 Itoa 函数,且 Itoa 函数是基于 FormatInt 函数的封装来的。
func main() {
// 整数转换成字符串
var age int64 = 90
// 转换 int64 类型
ageStr := strconv.FormatInt(age, 10)
fmt.Printf("%v, %T, %T\n", ageStr, age, ageStr)
score := 80
// 转换 int 乐行
scoreStr := strconv.Itoa(score)
fmt.Printf("%v, %T, %T\n", scoreStr, score, scoreStr)
}
执行上述代码,输出结果如下:
90, int64, string
80, int, string
字符串转换成整数可以使用 strconv 包下的 Atoi 函数,也可以使用内置的 string 函数。
浮点数转换成字符串
使用 FormatFloat 函数可以将 浮点数转换成字符串
func main() {
// 浮点转换成字符串
var balance float64 = 3.1415926
balanceStr := strconv.FormatFloat(balance, 'f', 2, 64)
fmt.Printf("%v, %T, %T\n", balanceStr, balance, balanceStr)
}
执行上述代码,输出结果如下:
3.14, float64, string
FormatFloat 函数包含四个参数,第一个参数是要转换的值,第二个参数时格式化类型,第三个参数时保留的小数点,如果为 -1 表示不对小数点个格式化,第四个参数时bit size,使用数字表示,如果要转换 float32 就填写 32,如果要转换 float64 就填写 64
布尔值转换成字符串
布尔类型转换为字符串要使用 FormatBool 函数
func main() {
// 布尔转换成字符串
isTrue := true
isTrueStr := strconv.FormatBool(isTrue)
fmt.Printf("%v, %T, %T", isTrueStr, isTrue, isTrueStr)
}
执行上述代码,输出结果如下:
true, bool, string
二、数组
数组介绍
数组是指一系列同类型数据类型的集合,数组中的元素可以任意类型,数组中包含的元素的数量就是数组的长度,长度也是数组类型的一部分。
数组需要占用连续的内存空间,且长度固定,与数组相对应的数据类型是切片,切片长度不固定,是一种可扩展的序列,类似 Java 中的 ArrayList,支持自动扩容。
数组定义以及遍历
数组定义可以使用 := 来自动判断类型,也可以是通过 var 关键字来定义并初始化数组
func main() {
// 定义数组
var emperors [3]string
emperors[0] = "朱棣"
emperors[1] = "朱高炽"
emperors[2] = "朱瞻基"
// 定义并初始化
var scores = [3]int{80, 90, 100}
// 定义并初始化,无须写数组长度,可自动判断
var ages = [...]int{10, 20, 30}
// 定义并初始化
seasons := [4]string{"春", "夏", "秋", "冬"}
fmt.Println(scores)
fmt.Println(emperors)
fmt.Println(seasons)
fmt.Println(ages)
// 遍历数组方式一
for i := 0; i < len(emperors); i++ {
fmt.Println(string(emperors[i]))
}
// 遍历数组方式二
for idx, item := range scores {
fmt.Println(idx, "-", item)
}
}
执行上述代码,输出结果如下:
[80 90 100]
[朱棣 朱高炽 朱瞻基]
[春 夏 秋 冬]
[10 20 30]
朱棣
朱高炽
朱瞻基
0 - 80
1 - 90
2 - 100
遍历字符串与遍历数组的方法是一致的。