go学习2

基础包介绍

  • strings

strings主要针对utf-8 编码,实现一些简单函数,字符串的很多函数基本方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/是否包含指定的字符串中任意一个字符 有一个出现过 就返回true
fmt.Println(strings.ContainsAny(s1,"glass"))

//返回指定字符出现的次数
fmt.Println(strings.Count(s1,"g"))

//文本的开头
fmt.Println(strings.HasPrefix(s1,"ok"))
//文本的结尾
fmt.Println(strings.HasSuffix(s1,".txt"))

//查找指定字符在字符串中存在的位置 如果不存在返回-1
fmt.Println(strings.Index(s1,"g"))
//查找字符中任意一个字符出现在字符串中的位置
fmt.Println(strings.IndexAny(s1,"s"))
//查找指定字符出现在字符串中最后一个的位置
fmt.Println(strings.LastIndex(s1,"s"))

//字符串的拼接
s2:=[]string{"123n","abc","ss"}
s3:=strings.Join(s2,"_")
fmt.Println(s3)// 123n_abc_ss

//字符串的切割
s4:=strings.Split(s3,"_")
fmt.Println(s4)// 返回切片[]string{"123n","abc","ss"}

//字符串的替换
s5 := "okoletsgo"
s6 := strings.Replace(s5, "o", "*", 1)
fmt.Println(s6)//*koletsgo
//TODO 1 只替换1次, -1 全部替换

//字符串的截取
//str[start:end]包含start 不包含end
  • strconv :主要用于字符串和基本类型的数据类型的转换

  • time: time包操作的都是时间,时间的单位都包括年,月,日,时,分,秒,毫秒,微妙,纳秒,皮秒

    1
    2
    3
    4
    5
    6
    7
    t := time.Now()
    fmt.Println(t) //2020-03-31 21:26:01.7307507 +0800 CST m=+0.001999001
    //获取的时间后面的信息是时区

    //上面的时间看起来不是很方便 于是需要格式化时间
    s := t.Format("2006年1月3日 15:04:05")
    fmt.Println(s)

需要注意的室Go语言中时间的格式化,需要指定格式化时间的模板, 不管年月日的类型格式怎么写,但是具体的数值必须写成2006-01-02 15:04:05, 如果不是这个日期就不能够格式化,这个时间也是为了纪念Go语言诞生的时间。

包管理

早期的Golang被很多开发者所诟病的一个问题就是依赖包的管理。Golang 1.5 release版本的发布之前,只能通过设置多个GOPATH的方式来解决这个问题,例如:我两个工程都依赖了Beego,但A工程依赖的是Beego1.1,B工程依赖的是Beego1.7,我必须设置两个GOPATH来区分,并且在切换工程的时候GOPATH也得切换,无比痛苦。终于终于在Golang 1.5 release 开始支持除了GOROOT和GOPATH之外的依赖查询,那就是vender

但是现在模块管理,包管理一般都是使用mod,go官方推荐的。

环境变量中可以增加GOPROXY=https://goproxy.io 这样没有梯子的情况下可以正确的加载相应的包文件。

函数

我们自定义函数的时候,需要注意按照规则定义必须满足以下格式,函数的名字可以由字母和数字组成,但是不能是数字开头,函数的首字母区分大小写,如果是大写的表示公共的函数,其他包内可以调用到,相当于其他语言中的public 前提是在别的包中引入了当前包。如果是小写的,表示私有的函数,仅能够在本包中调用,相当于其他语言中的private。

函数可以定义多个返回值,并且返回值类型,返回值数量都必须是一一对应的,return是将结果返回到函数的调用处,结束函数的执行。 _ 空白标识符,舍弃返回的数据。

1
2
3
4
5
6
7
8
9
func main() {
s1:=[2]string{}
s3,_:=join(s1)
fmt.Println(s3)
}
func join(ele [2]string) ([2]string,int) {

return ele,12
}

函数也可以声明为一个变量。

1
2
c:=join
c(s1)

匿名函数

1
2
3
4
5
6
7
8
9
10
res:=func(a int,b int) int{
fmt.Println(a+b)
return a+b
}(12,13)
fmt.Println(res)

a:=func(a, b int) int {
return a + b
}
fmt.Println(a)

加了后面的(),才算调用,不然不算调用。

  • 匿名函数可以作为另一个函数的参数
  • 匿名函数可以作为另一个函数的返回值
  • 根据go语言的数据类型的特点,函数也是一种类型,所以可以将一个函数作为另一个函数的参数传递func1()和func2()是两个函数,将func1函数作为func2这个函数的参数,func2函数就叫做高阶函数,因为他接收了一个函数作为参数。所以func1叫做回调函数,他作为另一个函数的参数。

defer

defer 表示延时推迟的意思,在go语言中用来延时一个函数或者方法的执行。如果一个函数或者方法添加了defer关键字,表示则暂时不执行,等到主函数的所有方法都执行完后才开始执行。

当多个函数被defer的时候他们被添加到一个堆栈中,并且根据先进后出的原则执行。 即 Last In First Out(LIFO)

defer函数调用时候,参数已经传递了,只不过代码暂时不执行而已。等待主函数执行结束后,才会去执行。

闭包

一个外层函数当中有内层函数,这个内层函数会操作外层函数的局部变量。并且,外层函数把内层函数作为返回值,则这里内层函数和外层函数的局部变量,统称为 闭包结构 。 这个外层函数的局部变量的生命周期会随着发生改变,原本当一个函数执行结束后,函数内部的局部变量也会随之销毁。但是闭包结构内的局部变量不会随着外层函数的结束而销毁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func main() {
res := closure()
fmt.Println(res) //0x49a880 返回内层函数函数体地址
r1 := res() //执行closure函数返回的匿名函数
fmt.Println(r1) //1
r2 := res()
fmt.Println(r2) //2
//普通的函数应该返回1,而这里存在闭包结构所以返回2 。
//一个外层函数当中有内层函数,这个内层函数会操作外层函数的局部变量,并且外层函数把内层函数作为返回值,则这里内层函数和外层函数的局部变量,统称为闭包结构。这个外层函数的局部变量的生命周期会发生改变,不会随着外层函数的结束而销毁。
//所以上面打印的r2 是累计到2 。

res2 := closure() //再次调用则产生新的闭包结构 局部变量则新定义的
fmt.Println(res2)
r3 := res2()//1
fmt.Println(r3)
r4 := res()
fmt.Println(r4)//3
fmt.Println(res())//4
}

//定义一个闭包结构的函数 返回一个匿名函数
func closure() func() int { //外层函数
//定义局部变量a
a := 0 //外层函数的局部变量
//定义内层匿名函数 并直接返回
return func() int { //内层函数
a++ //在匿名函数中将变量自增。内层函数用到了外层函数的局部变量,此变量不会随着外层函数的结束销毁
return a
}
}

指针

指针是存储另一个变量的内存地址的变量。 例如: 变量B的值为100, 地址为0x1122。变量A的值为变量B的地址0x1122,那么A就拥有了变量B的地址,则A就成为指针。Go语言中通过&获取变量的地址。通过*获取指针所对应的变量存储的数值。

1
2
3
4
5
6
7
8
a := 2
var i *int //声明一个int类型的指针
fmt.Println(&a) //0xc00000c1c8
i = &a //将a的地址取出来放到i里面
fmt.Println(&i) //0xc000006028
var a2 **int //声明一个指针类型的指针
a2 = &i //再把i的地址放进a2里面
fmt.Println(a2) //获取的是a2所对应的数值0xc000006028也就是i的地址

指针的指针,无线套娃。

1
2
3
4
5
6
7
8
9
//创建一个普通的数组
arr := [3]int{1, 2, 3}
fmt.Println(arr)

//创建一个指针 用来存储数组的地址 即:数组指针
var p *[3]int
p = &arr //将数组arr的地址,存储到数组指针p上。
fmt.Println(p[0]) //数组的指针 &[1 2 3] 后面跟数组的内容
fmt.Println((*p)[0]) //数组的指针 &[1 2 3] 后面跟数组的内容

直接传递数组是传递的数据,传递指针,才是传递真实数据