GO语言的进阶之路-Golang高级数据结构定义
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
我们之前学习过Golang的基本数据类型,字符串和byte,以及rune也有所了解,但是说起高级点的数据类型,可能我们还是不太清楚,那么今天就跟着我脚步一起学习一下这些高级数据类型数据吧。相信有部分人可能学习过Python,那么我这篇博客基本上不用看了,因为对你来说会觉得so easy。因为太多的相似之处了,只是写法不同。本章主要介绍数组(array),切片(scice),字典(map),结构体(struct)等等。
一.数组
有可能你学习过shell或是python,其实从输出的角度上来说,两者区别不大,但是Golang的数组那是别有一番风味啊,首先在学习数组之前,你要了解数组的两个参数重要参数,一个是数组的长度,一个是数组的容量。只要你明白了golang语言中数组这两个性质,那么在定义的数组时你就会跳过一些坑。比如说你想把容量为3的数组赋值给容量为10的数组,是不可行的,因为容量为三的数组,其长度是3,容量为10的数组,其长度是10,(如果你想把一个数组赋值给另一个数组,首先要让数组的长度相等,其次两边的类型要一致)数组的长度是类型的一部分,所以容量为3的数组是无法赋值给容量为10的数组,就是因为其长度不同,当然你也可以说是类型不同导致。
最后我要强调的是在Golang定义一个数组后,这个数组的容量是没法改变的。
1.定义一个数组并循环看其初值;
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 func main() { 13 var num [3]int //表示定义一个容量为3的数组,如果没有赋初值的话默认就是"0". 14 fmt.Printf("该数组的第一个数字是:%d\n",num[0]) 15 fmt.Printf("该数组的最后一个数字是:%d\n",num[len(num)-1]) 16 for i,v := range num { 17 fmt.Printf("数组的下标是:%d,数组的下标对应的初值是: %d\n",i,v) 18 } 19 for _,v := range num { 20 fmt.Printf("数组的初值是:%d\n",v) 21 } 22 } 23 24 25 26 #以上代码执行结果如下: 27 该数组的第一个数字是:0 28 该数组的最后一个数字是:0 29 数组的下标是:0,数组的下标对应的初值是: 0 30 数组的下标是:1,数组的下标对应的初值是: 0 31 数组的下标是:2,数组的下标对应的初值是: 0 32 数组的初值是:0 33 数组的初值是:0 34 数组的初值是:0
2.数组的花式定义和赋值
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 func main() { 13 var num [5]int //先定义一个容量为5的数组num。 14 num = [5]int{1,3,5,7,9} //给这num数组赋值 15 fmt.Printf("num数组的元素是:%d\n\n",num) 16 17 var a [3]int = [3]int{1,2,3} //将一个容量为三长度也为3的数组赋值给另一个容量为三的数组. 18 fmt.Printf("a数组的元素是:%d\n",a) 19 fmt.Printf("a[1]所对应的值是:%d\n",a[1]) //表示取a数组下标对应是1的value. 20 fmt.Printf("a数组的容量是:%d,该容量的长度是:%d,还可以存取%d个成员\n\n",cap(a),len(a),(cap(a)-len(a))) //cap函数用于计算数组的容量,len函数用于计算数组的长度. 21 22 23 b := [...]int{1,2,3,4} //这种定义方式其实就是不写具体的容量参数,那么容量的值就和长度是相等的。 24 fmt.Printf("b数组的元素是:%d\n",b) 25 fmt.Printf("该数组的容量是:%d,该容量的长度是:%d,还可以存取%d个成员\n\n",cap(b),len(b),(cap(b)-len(b))) 26 27 c := [...]int{4:20,7:-1} //定义下标为4的值为20,下标为7的值为-1。给指定数组下标赋初值,数组的长度为最大下标的加1,如果一个数组没有写明容量的话,会根据其下标最大的元素来定义其容量和长度。 28 fmt.Printf("c数组的元素是:%d\n",c) 29 fmt.Printf("该数组的容量是:%d,该容量的长度是:%d,还可以存取%d个成员\n\n",cap(c),len(c),(cap(c)-len(c))) 30 } 31 32 33 #以上代码执行结果如下: 34 num数组的元素是:[1 3 5 7 9] 35 36 a数组的元素是:[1 2 3] 37 a[1]所对应的值是:2 38 a数组的容量是:3,该容量的长度是:3,还可以存取0个成员 39 40 b数组的元素是:[1 2 3 4] 41 该数组的容量是:4,该容量的长度是:4,还可以存取0个成员 42 43 c数组的元素是:[0 0 0 0 20 0 0 -1] 44 该数组的容量是:8,该容量的长度是:8,还可以存取0个成员
3.数组的内存大小以及内存地址的查看;
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "fmt" 12 "unsafe" 13 ) 14 15 func main() { 16 array1 := [4]int{1,2,3} //定义array2这个数组,开辟了一款内存。 17 fmt.Printf("array1的元素是:%d\n",array1) 18 fmt.Printf("array1数组所占内存是:%d bytes\n",unsafe.Sizeof(array1)) //一个数组占有8个字节,容量为4的数组其内存是就是32字节 19 var array2 [4]int //定义一个 20 array2 = array1 21 fmt.Printf("array1的地址是:%d\narray2的地址是:%d\n",&array1[0],&array2[0]) 22 23 var n1,n2 int 24 n1 = 100 25 n2 = n1 //定义的n1和n2都是单独的容器,他们的内存地址是不一样的哟! 26 fmt.Printf("n1的内存地址是:%d\nn2的内存地址是:%d\n",&n1,&n2) //打印n1和n2的内存地址 27 28 fmt.Println(n2) 29 fmt.Println(n1 == n2) //这是判断两个变量对应的值是否相同!如果是就为真(true),是否不是九尾假(false) 30 } 31 32 33 #以上代码输出结果如下: 34 array1的元素是:[1 2 3 0] 35 array1数组所占内存是:32 bytes 36 array1的地址是:825741296640 37 array2的地址是:825741296768 38 n1的内存地址是:825741271528 39 n2的内存地址是:825741271536 40 100 41 false
4.字节数组
其实我们在之前就用过关于数组的东西,比如字节数组“[]byte”,其实它就是一个数组
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "crypto/md5" 12 "fmt" 13 ) 14 15 func main() { 16 data := []byte("yinzhengjie") //定义一个byte数组. 17 md5sum := md5.Sum(data) //调用Golang的md5算法将字节数组换算成一个唯一的md5值用于文件校验。 18 fmt.Printf("%x\n",md5sum) //打印其的md5值 19 fmt.Printf("%x\n",255) //一个十六进制的数字的取之范围是"00-FF",所以2个16进制表示一个字符。md5就是由十六进制的数字组成的。 20 } 21 22 23 24 #以上代码执行结果如下: 25 a1424987f80af77e96f540ccda1e68e5 26 ff
5.数组的应用
1 [root@yinzhengjie ~]# more md5.go 2 /* 3 #!/usr/bin/env gorun 4 @author :yinzhengjie 5 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 6 EMAIL:y1053419035@qq.com 7 */ 8 9 10 11 package main 12 13 import ( 14 "io/ioutil" 15 "fmt" 16 "os" 17 "crypto/md5" 18 ) 19 20 func main() { 21 var s string 22 for i := 1; i < len(os.Args); i++ { 23 s = os.Args[i] 24 printFile(s) 25 } 26 } 27 28 func printFile(name string) { 29 buf, err := ioutil.ReadFile(name) //读取文件的内容传给buf,当然它接受到的数据时仍然是字节,即[]uint8. 30 if err != nil { 31 fmt.Println(err) 32 return 33 } 34 md5sum := md5.Sum(buf) //把字节buf的值用md5算法算出其md5值。 35 fmt.Printf("经计算,文件'%v'的MD5值是:%x\n",os.Args[1],md5sum) 36 } 37 [root@yinzhengjie ~]# 38 [root@yinzhengjie ~]# go run md5.go startup.cfg 39 经计算,文件'startup.cfg'的MD5值是:c577d25cb647991e2b44e12c67649fcc 40 [root@yinzhengjie ~]#
二.切片
Golang的切片长得和数组很像,我们可以对一个数组做切片。要注意的是:当我们对一个数组做切片的时候,如果我们修改了切片下标所对应的值,那么被切片的数组的值也会跟着改变,因为他们都指向了同一块内存地址。
1.对数组做切片操作;
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "fmt" 12 ) 13 14 func main() { 15 primes := [8]int{2,3,5,7,9,11,13,15,} //定义一个数组 16 fmt.Printf("`primes`数组的值:%d\n",primes) 17 var sum []int = primes[1:4] //定义一个切片 18 fmt.Printf("`sum`切片的值:%d\n",sum) 19 fmt.Printf("`sum[0]`所对应的内存地址是:%x\n",&sum[0]) 20 fmt.Printf("`primes[1]`所对应的内存地址是:%x\n",&primes[1]) 21 var s1 []int 22 s1 = sum 23 fmt.Printf("`s1`切片对应的值为:%d\n",s1) 24 fmt.Printf("s1[0] == sum[0]为:%v\n",&s1[0] == &sum[0]) 25 } 26 27 28 29 #以上代码输出结果如下: 30 `primes`数组的值:[2 3 5 7 9 11 13 15] 31 `sum`切片的值:[3 5 7] 32 `sum[0]`所对应的内存地址是:c042046088 33 `primes[1]`所对应的内存地址是:c042046088 34 `s1`切片对应的值为:[3 5 7] 35 s1[0] == sum[0]为:true
2.切片的原理;
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 func main() { 13 names := [4]string{ //定义了一个字符串数组 14 "尹正杰", 15 "百度", 16 "谷歌", 17 "FQ", 18 } 19 fmt.Println(names) 20 21 a := names[0:2] 22 b := names[1:3] 23 fmt.Println(a,b) 24 25 b[0] = "xxx" //修改b的元素,会将names的对应的地址做相应的修改。 26 fmt.Println(a,b) 27 fmt.Println(names) 28 } 29 30 31 #以上代码输出结果如下: 32 [尹正杰 百度 谷歌 FQ] 33 [尹正杰 百度] [百度 谷歌] 34 [尹正杰 xxx] [xxx 谷歌] 35 [尹正杰 xxx 谷歌 FQ]
3.切片的字面量
嗨,可能你有可能听不懂“字面量”,好吧,其实它就是对切片做初始化赋值,仅此而已!
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 func main() { 13 num := []int{100,200,300,400,500} //切片的初始化方法,专业术语叫做切片字面量。 14 fmt.Println(num) 15 16 r := []bool{true,false,true,true} 17 fmt.Println(r) 18 } 19 20 21 22 #以上代码输出结果如下: 23 [100 200 300 400 500] 24 [true false true true]
4.切片的花式玩法;
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 func main() { 13 num := []int{2,3,5,7,9,11,13} //定义一个切片 14 fmt.Println(num) 15 num = num[1:4] //第一次对切片做切片操作,取值结果为:[3 5 7] 16 fmt.Println(num) 17 num = num[:2] //第二次切了又切,取值结果为[3 5] 18 fmt.Println(num) 19 num = num[1:] //第三次是在第二次切片操作后又一次切片操作,取值结果为[5] 20 fmt.Println(num) 21 } 22 23 24 25 #以上操作结果如下: 26 [2 3 5 7 9 11 13] 27 [3 5 7] 28 [3 5] 29 [5]
5.空切片;
切片包括2个属性,即长度和容量.因此我们不能看两个切片的长度为0就说这2个变量是相等的。
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 func main() { 13 num := []int{1,2,3} //定义一个切片 14 var s []int //定义一个空切片 15 fmt.Printf("`s`的值为:%v;长度为:%d;容量为%d\n",s,len(s),cap(s)) 16 if s == nil { 17 fmt.Printf("s为空\n") 18 } 19 s1 := num[:0] //将切片num的值赋值给s1. 20 fmt.Printf("`s1`的值为:%v;长度为:%d;容量为%d\n",s1,len(s1),cap(s1)) 21 fmt.Println(s1 == nil) //虽然s1的值为[],长度为0,但是容量为3,因此该切片不是空切片!切片包括2个属性,即长度和容量。 22 } 23 24 25 26 #以上代码输出结果如下: 27 `s`的值为:[];长度为:0;容量为0 28 s为空 29 `s1`的值为:[];长度为:0;容量为3 30 false
6.切片的追加操作
切片和数组不同,数组没有网容器里添加元素的方法,但是切片可以的。也就是说,只要切片的容量固定,我们可以根据容量大小往里添加数据相应的元素。
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 func main() { 13 var s []int //定义一个空切片s. 14 s = []int{1,2,3} //给这个空切片s赋值. 15 slice_attribute(s) 16 s = append(s,0) //往切片s追加一个"0"元素。 17 slice_attribute(s) 18 s = append(s,2,3,4) //继续往切片s追加“2,3,4”等元素。 19 slice_attribute(s) 20 21 } 22 23 func slice_attribute(s []int) { 24 fmt.Printf("len=%d cap=%d %v\n",len(s),cap(s),s) //打印切片的长度,容量以及对应的value. 25 } 26 27 28 29 30 #以上代码输出结果如下: 31 len=3 cap=3 [1 2 3] 32 len=4 cap=6 [1 2 3 0] 33 len=7 cap=12 [1 2 3 0 2 3 4]
7.用make函数定义一个切片;
make函数不仅仅可以定义一个切片,还可以定义一个map(你可以理解成字典)。
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 func main() { 13 slice1 := make([]int,5) //表示定义一个长度为5的切片 14 my_slice("slice1",slice1) 15 slice2 := make([]int,0,5) //表示定义一个长度为0,容量为5的切片 16 my_slice("slice2",slice2) 17 slice3 := slice2[:2] 18 my_slice("slice3",slice3) 19 slice4 := slice3[2:5] 20 my_slice("slice4",slice4) 21 } 22 23 func my_slice(s string ,x []int) { 24 fmt.Printf("`%s`切片长度为:%d 切片容量为:%d 切片中的元素是:%v\n",s,len(x),cap(x),x) 25 } 26 27 28 #以上代码执行结果如下: 29 `slice1`切片长度为:5 切片容量为:5 切片中的元素是:[0 0 0 0 0] 30 `slice2`切片长度为:0 切片容量为:5 切片中的元素是:[] 31 `slice3`切片长度为:2 切片容量为:5 切片中的元素是:[0 0] 32 `slice4`切片长度为:3 切片容量为:3 切片中的元素是:[0 0 0]
8.小试牛刀;
好了,关于切片的基本上这些就够用了,我们可以来小试牛刀一下啦~看看你掌握了多少;
A.反转切片的值;
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 func main() { 13 var num []int 14 num = []int{1,3,5,7} 15 fmt.Printf("切片反转之前的顺序是:%d\n",num) 16 my_rerversal(num) 17 fmt.Printf("切片反转之后的顺序是:%d\n",num) 18 } 19 20 func my_rerversal(s []int) { //该函数用于反转 21 for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 22 s[i], s[j] = s[j], s[i] 23 } 24 } 25 26 27 #以上代码执行结果如下: 28 切片反转之前的顺序是:[1 3 5 7] 29 切片反转之后的顺序是:[7 5 3 1]
B.随机反转切片的值;
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 9 package main 10 11 import ( 12 "bufio" 13 "os" 14 "fmt" 15 16 "strconv" 17 ) 18 19 var ( 20 s string 21 line string 22 ) 23 func main() { 24 f := bufio.NewReader(os.Stdin) 25 num := []int{100,200,300,400,500,600,700,800} 26 fmt.Printf("现有一些数字:·\033[32;1m%v\033[0m·\n",num) 27 for { 28 fmt.Print("请您想要反转下标的起始的位置>") 29 line,_ = f.ReadString('\n') 30 if len(line) == 1 { 31 continue //过滤掉空格; 32 } 33 fmt.Sscan(line,&s) 34 if s == "stop" { 35 break //定义停止程序的键值; 36 } 37 index,err := strconv.Atoi(s) 38 if err != nil { 39 fmt.Println("对不起,您必须输入一个数字") 40 } 41 num1 := num[:index] 42 num2 := num[index:] 43 i := 0 44 for { 45 46 num2=append(num2, num1[i]) 47 i = i + 1 48 if i >= len(num1) { 49 break 50 } 51 } 52 fmt.Printf("反转后的内容是·\033[31;1m%v\033[0m·\n",num2) 53 } 54 } 55 56 57 58 #以上代码输出结果如下: 59 现有一些数字:·[100 200 300 400 500 600 700 800]· 60 请您想要反转下标的起始的位置>1 61 反转后的内容是·[200 300 400 500 600 700 800 100]· 62 请您想要反转下标的起始的位置>3 63 反转后的内容是·[400 500 600 700 800 100 200 300]· 64 请您想要反转下标的起始的位置>5 65 反转后的内容是·[600 700 800 100 200 300 400 500]· 66 请您想要反转下标的起始的位置>7 67 反转后的内容是·[800 100 200 300 400 500 600 700]· 68 请您想要反转下标的起始的位置> 69 请您想要反转下标的起始的位置> 70 请您想要反转下标的起始的位置>2 71 反转后的内容是·[300 400 500 600 700 800 100 200]· 72 请您想要反转下标的起始的位置>4 73 反转后的内容是·[500 600 700 800 100 200 300 400]· 74 请您想要反转下标的起始的位置>6 75 反转后的内容是·[700 800 100 200 300 400 500 600]· 76 请您想要反转下标的起始的位置>8 77 反转后的内容是·[100 200 300 400 500 600 700 800]· 78 请您想要反转下标的起始的位置> 79 请您想要反转下标的起始的位置>
C.单词反转;
想要实现单词反转,有个package你必须虚得了解,那就是strings包。只要你知道这个packge基本上就能搞定这个事情啦!
hello world Golang! 尹正杰
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "io/ioutil" 12 "log" 13 "fmt" 14 "strings" 15 ) 16 17 func main() { 18 buf, err := ioutil.ReadFile("D:\\Golang环境\\Golang Program\\Golang lesson\\Day4\\你好.txt") 19 if err != nil { 20 log.Fatal(err) 21 } 22 fmt.Println(string(buf)) 23 str := strings.Fields(string(buf)) 24 my_rerversal(str) 25 26 } 27 28 29 func my_rerversal(s []string) { 30 for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 31 s[i], s[j] = s[j], s[i] 32 } 33 str1 := strings.Join(s," ") //把切片转换成字符串。 34 str1 = str1 35 fmt.Println(str1) 36 } 37 38 39 40 #以上代码执行结果如下: 41 hello world Golang! 尹正杰 42 尹正杰 Golang! world hello
三.map(你可以理解为字典。)
可能大家刚刚接触Golang的小伙伴都会跟我一样,这个map是干嘛的,是函数吗?学过python的小伙伴可能会想到map这个函数。其实它就是Golang中的字典。下面跟我一起看看它的特性吧。
A.hash方式的;
B.无序的;
C.0(1)的访问时间;
扩充: (n):存n个元素就需要转n圈目的数据;
0(n^2):存n个元素就需要n的平方圈目的数据;
0(2^n):存n个元素就需要2的n次方圈才能访问到目的数据;
0(1):不管存多少个元素,只转一圈就能找到(其实就是类似与python中的字典,有专用的key绑定一个value值。);
0(logn):排序查找,比入10000个元素中我们想要查数字10,那么可能只要查10次就查出来来;
1.定义一个map;
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 func main() { 13 info := make(map[string]int) //定义一个空字典,其key的数据类型是字符串,其value对应的数据是数字 14 info["age"] = 25 //往空字典中传值 15 info["size"] = 18 16 fmt.Println(info["age"]) //查看key所对应的value. 17 info["age"] = info["size"] + 100 //其实是数字相加运算 18 fmt.Println(info["age"]) 19 info2 := map[string]string{ 20 "name" : "尹正杰", 21 "age" : "25", 22 } 23 24 c,ok := info2["c"] //判断key是否在info2中 25 if ok { 26 fmt.Println(c) 27 }else { 28 fmt.Println("真不好意思,你所说的key在我的字典里压根不存在!") 29 } 30 fmt.Println(info2) 31 32 if hight,ok := info["d"];ok { //判断key是否在info中 33 fmt.Println(hight) 34 }else { 35 info["hight"] = 111 36 } 37 fmt.Println(info) 38 } 39 40 41 42 #以上代码执行结果如下: 43 25 44 118 45 真不好意思,你所说的key在我的字典里压根不存在! 46 map[name:尹正杰 age:25] 47 map[age:118 size:18 hight:111]
2.删除一个map;
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 func main() { 13 dict:= map[string]int{ 14 "a" :1, 15 } 16 fmt.Println(dict) 17 delete(dict,"a") //删除map中key所对应的value. 18 fmt.Println(dict) 19 20 var dict_1 map[string]int 21 fmt.Println(dict_1 == nil) //定义了一个空字典,内容为空 22 fmt.Println(dict_1) 23 dict_1 = make(map[string]int) //如果m1等于空(nil),需要重新m1才能用m1,不能直接对其赋值 24 dict_1["c"]=100 25 fmt.Println(dict_1) 26 } 27 28 29 30 #以上代码执行结果如下: 31 map[a:1] 32 map[] 33 true 34 map[] 35 map[c:100]
3.遍历map;
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 func main() { 13 ages := map[string]string{ 14 "姓名":"尹正杰", 15 "年龄":"25", 16 } 17 for i,j := range ages { //遍历key和value。 18 fmt.Println("key=",i,"value=",j) 19 } 20 21 for i := range ages { //只遍历key. 22 fmt.Println(i) 23 } 24 } 25 26 27 #以上代码执行结果如下: 28 key= 姓名 value= 尹正杰 29 key= 年龄 value= 25 30 姓名 31 年龄
4.小试牛刀;
A.统计单词出现的频率;
Scanner provides a convenient interface for reading data such as a file of newline-delimited lines of text Successive
calls to the Scan method will step through the tokens of a file skipping the bytes between the tokens The specification
of a token is defined by a split function of type SplitFunc the default split function breaks the input into lines with
line termination stripped Split functions are defined in this package for scanning a file into lines bytes UTF-8-encoded
runes and space-delimited words The client may instead provide a custom split function
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "fmt" 12 "io/ioutil" 13 "log" 14 "strings" 15 ) 16 17 func main() { 18 buf,err := ioutil.ReadFile("D:\\Golang环境\\Golang Program\\Golang lesson\\Day4\\单词.txt") 19 if err != nil { 20 log.Fatal(err) 21 } 22 statistic_times := make(map[string]int) 23 words_length := strings.Fields(string(buf)) 24 25 for counts,word := range words_length { 26 word,ok :=statistic_times[word] //判断key是否存在,这个word是字符串,这个counts是统计的word的次数。 27 if ok{ 28 word = word //我这里是重新赋值,因为上面定义了,下面必须用这个变量,不然就报错,有大神可以帮忙优化一下这里。 29 statistic_times[words_length[counts]] = statistic_times[words_length[counts]] + 1 30 }else { 31 statistic_times[words_length[counts]] = 1 32 } 33 } 34 for word,counts := range statistic_times { 35 fmt.Println(word,counts) 36 } 37 } 38 39 40 41 #以上代码输出结果如下: 42 43 lines 3 44 method 1 45 functions 1 46 in 1 47 words 1 48 may 1 49 newline-delimited 1 50 the 6 51 package 1 52 provides 1 53 will 1 54 step 1 55 through 1 56 skipping 1 57 stripped 1 58 Scanner 1 59 as 1 60 tokens 2 61 specification 1 62 space-delimited 1 63 client 1 64 function 3 65 instead 1 66 for 2 67 of 5 68 by 1 69 termination 1 70 interface 1 71 between 1 72 breaks 1 73 with 1 74 line 1 75 runes 1 76 Successive 1 77 SplitFunc 1 78 into 2 79 Split 1 80 reading 1 81 defined 2 82 type 1 83 default 1 84 input 1 85 custom 1 86 text 1 87 to 1 88 Scan 1 89 bytes 2 90 is 1 91 such 1 92 token 1 93 are 1 94 file 3 95 The 2 96 split 3 97 UTF-8-encoded 1 98 and 1 99 provide 1 100 a 7 101 convenient 1 102 data 1 103 calls 1 104 this 1 105 scanning 1
四.struct(结构体);
说道结构体,大家可能会懵逼,不知道是个啥东西,其实我觉得Golang起的结构体这个名字还是蛮接地气的,不是吗?从字面意思就可以理解为一个数据的结构体系。基本上一听这个名字就大致知道是干嘛的,它就好似一个模板,让我们看清楚了她的各个主要分支结构。其实,在Python中,我们叫它实例,说白了,当初学习Python实例的和实例化的时候让我很懵逼,随着时间的推移我才明白什么是实例,什么是实例化。相信学习过Python的同学应该都知道class。其实结构体,我们就可以理解成Python中的定义一个实例,而用这个结构体的时候,我们就可以理解是在实例化这个对象。
1.定义一个结构体以及实例化结构体的两种方式;
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 type Student struct { //定义一个结构体,类似与python定义的一个实例 13 ID int 14 Name string 15 } 16 17 func main() { 18 var s Student //引用结构体,方式一。【可以理解为实例化】 19 s.ID = 100 20 s.Name = "yinzhengjie" 21 fmt.Println(s) 22 23 s1 := Student{ //引用结构体,方式二 24 ID:200, 25 Name:"饼干", 26 } 27 fmt.Println(s1) 28 } 29 30 31 #以上代码输出结果如下: 32 {100 yinzhengjie} 33 {200 饼干}
2.结构体的指针;
在Golang的全局变量中,我们声明一个变量的同时,还需要制定其数据类型,可以是int,string,byte,也可以是[]int,[]string,[]byte,还可以是我们自定义的结构体等等。
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 type Student struct { //定义一个结构体 13 ID int 14 Name string 15 } 16 17 func main() { 18 19 s1 := Student{ //引用(实例化)结构体 20 ID:100, 21 Name:"尹正杰", 22 } 23 fmt.Println(s1) 24 25 var p *Student //定义p为我们自定义的结构体类型 26 p = &s1 //将实例化的内存地址传给p 27 p.ID = 200 //修改结构体里面的ID参数为200,由于p是指针类型,故会修改s1的ID的值。 28 fmt.Println(s1) 29 30 var p1 *int 31 p1 = &s1.ID //我们也可以直接取到我们自定义的结构体重的参数的内存地址,然后给其赋值,也能达到修改参数的效果。 32 *p1 = 300 33 fmt.Println(s1) 34 } 35 36 37 #以上代码输出结果如下: 38 {100 尹正杰} 39 {200 尹正杰} 40 {300 尹正杰}
五.交互模式
一般用Golang写的运维工具都是自己把功能跑完,不需要每次手动执行一些命令才能完成工作。但是在这几道一些安全性信息的时候就不得不要求用户交互了,比如你登录QQ,登录你的爱奇艺会员,腾讯会员以及乐视会员等等。都需要你手动输入一些字符串。那么问题来了,Golang是如何实现这功能的呢?跟着我一起实验吧!
1.初探交互模式;
我们写一个交互程序,让用户输入什么就打印出来什么。
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 13 var cmd string 14 15 func main() { 16 for { //循环输入 17 fmt.Print("input>>") //输入的内容都是字符串类型。 18 fmt.Scan(&cmd) //获取用命令行中第一字符串传给cmd。 19 if cmd == "stop" { //定义结束循环的关键字 20 break 21 } 22 fmt.Println(cmd) //将输入的字符串cmd变量打印出来 23 } 24 } 25 26 27 #以上代码执行结果如下: 28 input>>尹正杰 29 尹正杰 30 input>>yinzhengjie 31 yinzhengjie 32 input>>您好 33 您好 34 input>>hello 35 hello 36 input>>world 37 world 38 input>>
2.获取一整行内容;
获取通过第一个交互模式你也体会到了,存在很多坑,比如不能把输出空格或者回车就会卡在那里不动了,无法获取完整的一行内容并打印,只能讲命令行的第一个位置参数给打印出来,那么如果将一整行的内容都打印出来呢?这个时候我们就需要对bufio这个package需要一定的掌握。
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 9 package main 10 11 import ( 12 "bufio" 13 "os" 14 "fmt" 15 "strings" 16 ) 17 18 var ( 19 s string 20 n int 21 line string 22 ) 23 func main() { 24 f := bufio.NewReader(os.Stdin) //读取输入的内容 25 for { 26 fmt.Print("请输入一些字符串>") 27 line,_ = f.ReadString('\n') //定义一行输入的内容分隔符。 28 if len(line) == 1 { 29 continue //如果用户输入的是一个空行就让用户继续输入。 30 } 31 line = strings.Replace(line,"\n"," ",-1) //利用string的修改操作,将换行符脱掉。 32 // 要注意的是它是需要单独占用内存的。其实这行没必要写,因为只需要把下一行的“\n”去掉就好使啦 33 // 即:fmt.Printf("您输入的是:%s",line),因为我刚刚学的时候踩了个坑,所以在这里记录下。 34 fmt.Printf("您输入的是:%s\n",line) 35 fmt.Sscan(line,&s,&n) //将s和n的值传给line,如果不传值的话就 36 if s == "stop" { 37 break 38 } 39 fmt.Printf("您输入的第一个参数是:·\033[31;1m%v\033[0m·,输入的第二个参数是··\033[31;1m%v\033[0m·.\n",s,n) 40 } 41 } 42 43 44 45 #意思代码输出结果如下: 46 请输入一些字符串> 47 请输入一些字符串> 48 请输入一些字符串>GOLANG 123 49 您输入的是:GOLANG 123 50 您输入的第一个参数是:·GOLANG·,输入的第二个参数是··123·. 51 请输入一些字符串>
六.序列化和反序列化(json)
说直白点,序列化就是讲数据写入硬盘,反序列化就是讲数据从硬盘读取出来。而存取方式又是用户选取的一个难题,每种语言都有自己的存取数据的方式,不过很多种语言都一款通用的存取方式,那就是json。也就是说很多语言都支持这种存储,说的在直白点就是你用Golang将数据用json存取,我可以用JAVA语言打卡这个文件内容,并获取到文件的内容做相应的处理。
1.序列化案例;
执行之前的“a.txt”文件内容
http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ {"ID":1,"Name":"yinzhengjie"}
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "encoding/json" 12 "log" 13 "fmt" 14 "os" 15 ) 16 17 type Student struct { 18 ID int 19 Name string 20 } 21 22 func main() { 23 s := Student{ 24 ID:1, 25 Name:"yinzhengjie", 26 } 27 buf,err := json.Marshal(s) //序列化一个结构体, 28 if err != nil { 29 log.Fatal("序列化报错是:%s",err) 30 } 31 f,err := os.OpenFile("D:\\Golang环境\\Golang Program\\Golang lesson\\Day4\\a.txt",os.O_APPEND|os.O_CREATE|os.O_RDWR,0644) 32 if err != nil { 33 log.Fatal(err) 34 } 35 f.WriteString(string(buf)) 36 f.WriteString("\n") 37 f.Close() 38 fmt.Println("写入成功") 39 } 40 41 42 43 #以上代码执行结果如下: 44 写入成功
2.反序列化之前的内容;
文件“b.txt”内容如下:
{100 yinzhengjie}
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "encoding/json" 12 "log" 13 "fmt" 14 "io/ioutil" 15 ) 16 17 type Student struct { 18 ID int 19 Name string 20 } 21 22 func main() { 23 buf,err := ioutil.ReadFile("D:\\Golang环境\\Golang Program\\Golang lesson\\Day4\\b.txt") 24 if err != nil { 25 fmt.Println("你愁啥?文件打开错误了!") 26 return 27 } 28 var str Student 29 err1 := json.Unmarshal(buf,&str) 30 if err1 != nil { 31 log.Fatal("反序列化报错啦:%s",err1) 32 } 33 fmt.Println(str) 34 } 35 36 37 #以上代码执行结果如下: 38 {100 yinzhengjie}
3.小试牛刀;
写一个简单的交互学生管理系统,需要运用的知识点有:map,文件处理,序列化,字符串处理,结构体等等,以下仅是初稿,暂时提供的一个思路。
1 [root@yinzhengjie tmp]# more student.go 2 /* 3 #!/usr/bin/env gorun 4 @author :yinzhengjie 5 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 6 EMAIL:y1053419035@qq.com 7 */ 8 9 package main 10 11 import ( 12 "bufio" 13 "fmt" 14 "os" 15 "log" 16 "io" 17 "encoding/json" 18 "strings" 19 ) 20 21 var ( 22 cmd string 23 name string 24 id int 25 line string 26 file_name string 27 ) 28 29 type Student struct { 30 ID int 31 Name string 32 } 33 34 func main() { 35 f := bufio.NewReader(os.Stdin) 36 for { 37 fmt.Print(" 请输入>>> ") 38 line, _ = f.ReadString('\n') 39 fmt.Sscan(line, &cmd) 40 if len(line) == 1 { 41 continue 42 } 43 switch cmd { 44 case "list": 45 list() 46 case "add": 47 add() 48 case "save": 49 save() 50 case "load": 51 load() 52 case "stop": 53 os.Exit(0) 54 default: 55 fmt.Println("您输出的命令无效") 56 } 57 } 58 } 59 60 func list() { 61 f,err := os.Open("student_info.json") //打开一个文件,如果这个文件不存在的话就会报错。 62 if err != nil { 63 log.Fatal(err) 64 } 65 r := bufio.NewReader(f) //取出文件的内容 66 for { 67 line,err := r.ReadString('\n') 68 if err == io.EOF{ 69 break 70 } 71 fmt.Print(line) 72 } 73 f.Close() 74 } 75 76 77 func add() { 78 fmt.Sscan(line, &cmd, &id, &name) 79 f, err := os.Open("student_info.json") //打开一个文件,如果这个文件不存在的话就会报错。 80 if err != nil { 81 log.Fatal(err) 82 } 83 r := bufio.NewReader(f) //取出文件的内容 84 flag := 0 //定义一个标志位,当输入的ID和name相同时,就将其的值改为1. 85 for { 86 line, err := r.ReadString('\n') 87 if err == io.EOF { 88 break 89 } 90 s := Student{ 91 ID: id, 92 Name: name, 93 } 94 buf, err := json.Marshal(s) 95 if err != nil { 96 log.Fatal("序列化报错是:%s", err) 97 } 98 line = strings.Replace(line,"\n","",-1) //将换行符替换为空,你可以理解是删除了换行符。 99 if line == string(buf) { 100 fmt.Println("对不起,您输入的用户或者ID已经存在了,请重新输入!") 101 flag = 1 102 break 103 104 } 105 } 106 if flag == 0 { 107 s := Student{ 108 ID:id, 109 Name:name, 110 } 111 buf,err := json.Marshal(s) //序列化一个结构体, 112 if err != nil { 113 log.Fatal("序列化报错是:%s",err) 114 } 115 fmt.Println(string(buf)) 116 f,err := os.OpenFile("student_info.json",os.O_APPEND|os.O_CREATE|os.O_RDWR,0644) 117 if err != nil { 118 log.Fatal(err) 119 } 120 f.WriteString(string(buf)) 121 f.WriteString("\n") 122 f.Close() 123 fmt.Println("写入成功") 124 125 } 126 } 127 func save() { 128 fmt.Sscan(line, &cmd,&file_name) 129 f,err := os.Open("student_info.json") //打开一个文件,如果这个文件不存在的话就会报错。 130 if err != nil { 131 log.Fatal(err) 132 } 133 r := bufio.NewReader(f) //取出文件的内容 134 f2,err := os.OpenFile(file_name,os.O_APPEND|os.O_CREATE|os.O_RDWR,0644) //打开一个新文件 135 if err != nil { 136 log.Fatal(err) 137 } 138 for { 139 line,err := r.ReadString('\n') 140 if err == io.EOF{ 141 break 142 } 143 f2.WriteString(line) //将student_info.json内容写到指定的文件中去。 144 } 145 f2.Close() 146 f.Close() 147 } 148 149 func load() { 150 fmt.Sscan(line, &cmd,&file_name) 151 f,err := os.Open(file_name) //打开一个文件,如果这个文件不存在的话就会报错。 152 if err != nil { 153 fmt.Println("对不起!系统没用找到该文件!") 154 return 155 } 156 r := bufio.NewReader(f) //取出文件的内容 157 for { 158 line,err := r.ReadString('\n') 159 if err == io.EOF{ 160 break 161 } 162 fmt.Print(line) 163 } 164 f.Close() 165 } 166 [root@yinzhengjie tmp]# 167 [root@yinzhengjie tmp]# 168 [root@yinzhengjie tmp]# 169 [root@yinzhengjie tmp]# 170 [root@yinzhengjie tmp]# go run student.go 171 请输入>>> 172 请输入>>> 173 请输入>>> list 174 {"ID":1,"Name":"bingan"} 175 {"ID":2,"Name":"yinzhengjie"} 176 {"ID":3,"Name":"jie"} 177 {"ID":5,"Name":"Golang"} 178 {"ID":4,"Name":"reboot"} 179 {"ID":10,"Name":"jie"} 180 {"ID":100,"Name":"dev"} 181 {"ID":200,"Name":"jay"} 182 请输入>>> 183 请输入>>> 184 请输入>>> add 300 GOOD 185 {"ID":300,"Name":"GOOD"} 186 写入成功 187 请输入>>> list 188 {"ID":1,"Name":"bingan"} 189 {"ID":2,"Name":"yinzhengjie"} 190 {"ID":3,"Name":"jie"} 191 {"ID":5,"Name":"Golang"} 192 {"ID":4,"Name":"reboot"} 193 {"ID":10,"Name":"jie"} 194 {"ID":100,"Name":"dev"} 195 {"ID":200,"Name":"jay"} 196 {"ID":300,"Name":"GOOD"} 197 请输入>>> 198 请输入>>> save 6666 199 请输入>>> 200 请输入>>> load 6666 201 {"ID":1,"Name":"bingan"} 202 {"ID":2,"Name":"yinzhengjie"} 203 {"ID":3,"Name":"jie"} 204 {"ID":5,"Name":"Golang"} 205 {"ID":4,"Name":"reboot"} 206 {"ID":10,"Name":"jie"} 207 {"ID":100,"Name":"dev"} 208 {"ID":200,"Name":"jay"} 209 {"ID":300,"Name":"GOOD"} 210 请输入>>> 211 请输入>>> stop 212 You have new mail in /var/spool/mail/root 213 [root@yinzhengjie tmp]#
有疑问加站长微信联系(非本文作者)