GO语言的进阶之路-Golang高级数据结构定义

时间:2022-03-21 16:21:55

                      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.定义一个数组并循环看其初值;

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
var num [3]int //表示定义一个容量为3的数组,如果没有赋初值的话默认就是"".
fmt.Printf("该数组的第一个数字是:%d\n",num[0])
fmt.Printf("该数组的最后一个数字是:%d\n",num[len(num)-1])
for i,v := range num {
fmt.Printf("数组的下标是:%d,数组的下标对应的初值是: %d\n",i,v)
}
for _,v := range num {
fmt.Printf("数组的初值是:%d\n",v)
}
} #以上代码执行结果如下:
该数组的第一个数字是:0
该数组的最后一个数字是:0
数组的下标是:0,数组的下标对应的初值是: 0
数组的下标是:1,数组的下标对应的初值是: 0
数组的下标是:2,数组的下标对应的初值是: 0
数组的初值是:0
数组的初值是:0
数组的初值是:0

2.数组的花式定义和赋值

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
var num [5]int //先定义一个容量为5的数组num。
num = [5]int{1,3,5,7,9} //给这num数组赋值
fmt.Printf("num数组的元素是:%d\n\n",num) var a [3]int = [3]int{1,2,3} //将一个容量为三长度也为3的数组赋值给另一个容量为三的数组.
fmt.Printf("a数组的元素是:%d\n",a)
fmt.Printf("a[1]所对应的值是:%d\n",a[1]) //表示取a数组下标对应是1的value.
fmt.Printf("a数组的容量是:%d,该容量的长度是:%d,还可以存取%d个成员\n\n",cap(a),len(a),(cap(a)-len(a))) //cap函数用于计算数组的容量,len函数用于计算数组的长度. b := [...]int{1,2,3,4} //这种定义方式其实就是不写具体的容量参数,那么容量的值就和长度是相等的。
fmt.Printf("b数组的元素是:%d\n",b)
fmt.Printf("该数组的容量是:%d,该容量的长度是:%d,还可以存取%d个成员\n\n",cap(b),len(b),(cap(b)-len(b))) c := [...]int{4:20,7:-1} //定义下标为4的值为20,下标为7的值为-1。给指定数组下标赋初值,数组的长度为最大下标的加1,如果一个数组没有写明容量的话,会根据其下标最大的元素来定义其容量和长度。
fmt.Printf("c数组的元素是:%d\n",c)
fmt.Printf("该数组的容量是:%d,该容量的长度是:%d,还可以存取%d个成员\n\n",cap(c),len(c),(cap(c)-len(c)))
} #以上代码执行结果如下:
num数组的元素是:[1 3 5 7 9] a数组的元素是:[1 2 3]
a[1]所对应的值是:2
a数组的容量是:3,该容量的长度是:3,还可以存取0个成员 b数组的元素是:[1 2 3 4]
该数组的容量是:4,该容量的长度是:4,还可以存取0个成员 c数组的元素是:[0 0 0 0 20 0 0 -1]
该数组的容量是:8,该容量的长度是:8,还可以存取0个成员

3.数组的内存大小以及内存地址的查看;

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import (
"fmt"
"unsafe"
) func main() {
array1 := [4]int{1,2,3} //定义array2这个数组,开辟了一款内存。
fmt.Printf("array1的元素是:%d\n",array1)
fmt.Printf("array1数组所占内存是:%d bytes\n",unsafe.Sizeof(array1)) //一个数组占有8个字节,容量为4的数组其内存是就是32字节
var array2 [4]int //定义一个
array2 = array1
fmt.Printf("array1的地址是:%d\narray2的地址是:%d\n",&array1[0],&array2[0]) var n1,n2 int
n1 = 100
n2 = n1 //定义的n1和n2都是单独的容器,他们的内存地址是不一样的哟!
fmt.Printf("n1的内存地址是:%d\nn2的内存地址是:%d\n",&n1,&n2) //打印n1和n2的内存地址 fmt.Println(n2)
fmt.Println(n1 == n2) //这是判断两个变量对应的值是否相同!如果是就为真(true),是否不是九尾假(false)
} #以上代码输出结果如下:
array1的元素是:[1 2 3 0]
array1数组所占内存是:32 bytes
array1的地址是:825741296640
array2的地址是:825741296768
n1的内存地址是:825741271528
n2的内存地址是:825741271536
100
false

4.字节数组

  其实我们在之前就用过关于数组的东西,比如字节数组“[]byte”,其实它就是一个数组

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import (
"crypto/md5"
"fmt"
) func main() {
data := []byte("yinzhengjie") //定义一个byte数组.
md5sum := md5.Sum(data) //调用Golang的md5算法将字节数组换算成一个唯一的md5值用于文件校验。
fmt.Printf("%x\n",md5sum) //打印其的md5值
fmt.Printf("%x\n",255) //一个十六进制的数字的取之范围是"00-FF",所以2个16进制表示一个字符。md5就是由十六进制的数字组成的。
} #以上代码执行结果如下:
a1424987f80af77e96f540ccda1e68e5
ff

5.数组的应用

 [root@yinzhengjie ~]# more md5.go
/*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import (
"io/ioutil"
"fmt"
"os"
"crypto/md5"
) func main() {
var s string
for i := 1; i < len(os.Args); i++ {
s = os.Args[i]
printFile(s)
}
} func printFile(name string) {
buf, err := ioutil.ReadFile(name) //读取文件的内容传给buf,当然它接受到的数据时仍然是字节,即[]uint8.
if err != nil {
fmt.Println(err)
return
}
md5sum := md5.Sum(buf) //把字节buf的值用md5算法算出其md5值。
fmt.Printf("经计算,文件'%v'的MD5值是:%x\n",os.Args[1],md5sum)
}
[root@yinzhengjie ~]#
[root@yinzhengjie ~]# go run md5.go startup.cfg
经计算,文件'startup.cfg'的MD5值是:c577d25cb647991e2b44e12c67649fcc
[root@yinzhengjie ~]#

二.切片

  Golang的切片长得和数组很像,我们可以对一个数组做切片。要注意的是:当我们对一个数组做切片的时候,如果我们修改了切片下标所对应的值,那么被切片的数组的值也会跟着改变,因为他们都指向了同一块内存地址。

1.对数组做切片操作;

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import (
"fmt"
) func main() {
primes := [8]int{2,3,5,7,9,11,13,15,} //定义一个数组
fmt.Printf("`primes`数组的值:%d\n",primes)
var sum []int = primes[1:4] //定义一个切片
fmt.Printf("`sum`切片的值:%d\n",sum)
fmt.Printf("`sum[0]`所对应的内存地址是:%x\n",&sum[0])
fmt.Printf("`primes[1]`所对应的内存地址是:%x\n",&primes[1])
var s1 []int
s1 = sum
fmt.Printf("`s1`切片对应的值为:%d\n",s1)
fmt.Printf("s1[0] == sum[0]为:%v\n",&s1[0] == &sum[0])
} #以上代码输出结果如下:
`primes`数组的值:[2 3 5 7 9 11 13 15]
`sum`切片的值:[3 5 7]
`sum[0]`所对应的内存地址是:c042046088
`primes[1]`所对应的内存地址是:c042046088
`s1`切片对应的值为:[3 5 7]
s1[0] == sum[0]为:true

2.切片的原理;

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
names := [4]string{ //定义了一个字符串数组
"尹正杰",
"百度",
"谷歌",
"*",
}
fmt.Println(names) a := names[0:2]
b := names[1:3]
fmt.Println(a,b) b[0] = "xxx" //修改b的元素,会将names的对应的地址做相应的修改。
fmt.Println(a,b)
fmt.Println(names)
} #以上代码输出结果如下:
[尹正杰 百度 谷歌 *]
[尹正杰 百度] [百度 谷歌]
[尹正杰 xxx] [xxx 谷歌]
[尹正杰 xxx 谷歌 *]

3.切片的字面量

  嗨,可能你有可能听不懂“字面量”,好吧,其实它就是对切片做初始化赋值,仅此而已!

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
num := []int{100,200,300,400,500} //切片的初始化方法,专业术语叫做切片字面量。
fmt.Println(num) r := []bool{true,false,true,true}
fmt.Println(r)
} #以上代码输出结果如下:
[100 200 300 400 500]
[true false true true]

4.切片的花式玩法;

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
num := []int{2,3,5,7,9,11,13} //定义一个切片
fmt.Println(num)
num = num[1:4] //第一次对切片做切片操作,取值结果为:[3 5 7]
fmt.Println(num)
num = num[:2] //第二次切了又切,取值结果为[3 5]
fmt.Println(num)
num = num[1:] //第三次是在第二次切片操作后又一次切片操作,取值结果为[5]
fmt.Println(num)
} #以上操作结果如下:
[2 3 5 7 9 11 13]
[3 5 7]
[3 5]
[5]

5.空切片;

  切片包括2个属性,即长度和容量.因此我们不能看两个切片的长度为0就说这2个变量是相等的。
 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
num := []int{1,2,3} //定义一个切片
var s []int //定义一个空切片
fmt.Printf("`s`的值为:%v;长度为:%d;容量为%d\n",s,len(s),cap(s))
if s == nil {
fmt.Printf("s为空\n")
}
s1 := num[:0] //将切片num的值赋值给s1.
fmt.Printf("`s1`的值为:%v;长度为:%d;容量为%d\n",s1,len(s1),cap(s1))
fmt.Println(s1 == nil) //虽然s1的值为[],长度为0,但是容量为3,因此该切片不是空切片!切片包括2个属性,即长度和容量。
} #以上代码输出结果如下:
`s`的值为:[];长度为:0;容量为0
s为空
`s1`的值为:[];长度为:0;容量为3
false

6.切片的追加操作

  切片和数组不同,数组没有网容器里添加元素的方法,但是切片可以的。也就是说,只要切片的容量固定,我们可以根据容量大小往里添加数据相应的元素。

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
var s []int //定义一个空切片s.
s = []int{1,2,3} //给这个空切片s赋值.
slice_attribute(s)
s = append(s,0) //往切片s追加一个""元素。
slice_attribute(s)
s = append(s,2,3,4) //继续往切片s追加“2,3,4”等元素。
slice_attribute(s) } func slice_attribute(s []int) {
fmt.Printf("len=%d cap=%d %v\n",len(s),cap(s),s) //打印切片的长度,容量以及对应的value.
} #以上代码输出结果如下:
len=3 cap=3 [1 2 3]
len=4 cap=6 [1 2 3 0]
len=7 cap=12 [1 2 3 0 2 3 4]

7.用make函数定义一个切片;

  make函数不仅仅可以定义一个切片,还可以定义一个map(你可以理解成字典)。

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
slice1 := make([]int,5) //表示定义一个长度为5的切片
my_slice("slice1",slice1)
slice2 := make([]int,0,5) //表示定义一个长度为0,容量为5的切片
my_slice("slice2",slice2)
slice3 := slice2[:2]
my_slice("slice3",slice3)
slice4 := slice3[2:5]
my_slice("slice4",slice4)
} func my_slice(s string ,x []int) {
fmt.Printf("`%s`切片长度为:%d 切片容量为:%d 切片中的元素是:%v\n",s,len(x),cap(x),x)
} #以上代码执行结果如下:
`slice1`切片长度为:5 切片容量为:5 切片中的元素是:[0 0 0 0 0]
`slice2`切片长度为:0 切片容量为:5 切片中的元素是:[]
`slice3`切片长度为:2 切片容量为:5 切片中的元素是:[0 0]
`slice4`切片长度为:3 切片容量为:3 切片中的元素是:[0 0 0]

8.小试牛刀;

  好了,关于切片的基本上这些就够用了,我们可以来小试牛刀一下啦~看看你掌握了多少;

A.反转切片的值;

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
var num []int
num = []int{1,3,5,7}
fmt.Printf("切片反转之前的顺序是:%d\n",num)
my_rerversal(num)
fmt.Printf("切片反转之后的顺序是:%d\n",num)
} func my_rerversal(s []int) { //该函数用于反转
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
} #以上代码执行结果如下:
切片反转之前的顺序是:[1 3 5 7]
切片反转之后的顺序是:[7 5 3 1]

B.随机反转切片的值;

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import (
"bufio"
"os"
"fmt" "strconv"
) var (
s string
line string
)
func main() {
f := bufio.NewReader(os.Stdin)
num := []int{100,200,300,400,500,600,700,800}
fmt.Printf("现有一些数字:·\033[32;1m%v\033[0m·\n",num)
for {
fmt.Print("请您想要反转下标的起始的位置>")
line,_ = f.ReadString('\n')
if len(line) == 1 {
continue //过滤掉空格;
}
fmt.Sscan(line,&s)
if s == "stop" {
break //定义停止程序的键值;
}
index,err := strconv.Atoi(s)
if err != nil {
fmt.Println("对不起,您必须输入一个数字")
}
num1 := num[:index]
num2 := num[index:]
i := 0
for { num2=append(num2, num1[i])
i = i + 1
if i >= len(num1) {
break
}
}
fmt.Printf("反转后的内容是·\033[31;1m%v\033[0m·\n",num2)
}
} #以上代码输出结果如下:
现有一些数字:·[100 200 300 400 500 600 700 800]·
请您想要反转下标的起始的位置>1
反转后的内容是·[200 300 400 500 600 700 800 100]·
请您想要反转下标的起始的位置>3
反转后的内容是·[400 500 600 700 800 100 200 300]·
请您想要反转下标的起始的位置>5
反转后的内容是·[600 700 800 100 200 300 400 500]·
请您想要反转下标的起始的位置>7
反转后的内容是·[800 100 200 300 400 500 600 700]·
请您想要反转下标的起始的位置>
请您想要反转下标的起始的位置>
请您想要反转下标的起始的位置>2
反转后的内容是·[300 400 500 600 700 800 100 200]·
请您想要反转下标的起始的位置>4
反转后的内容是·[500 600 700 800 100 200 300 400]·
请您想要反转下标的起始的位置>6
反转后的内容是·[700 800 100 200 300 400 500 600]·
请您想要反转下标的起始的位置>8
反转后的内容是·[100 200 300 400 500 600 700 800]·
请您想要反转下标的起始的位置>
请您想要反转下标的起始的位置>

C.单词反转;

  想要实现单词反转,有个package你必须虚得了解,那就是strings包。只要你知道这个packge基本上就能搞定这个事情啦!

hello world Golang! 尹正杰

你好.txt

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import (
"io/ioutil"
"log"
"fmt"
"strings"
) func main() {
buf, err := ioutil.ReadFile("D:\\Golang环境\\Golang Program\\Golang lesson\\Day4\\你好.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(buf))
str := strings.Fields(string(buf))
my_rerversal(str) } func my_rerversal(s []string) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
str1 := strings.Join(s," ") //把切片转换成字符串。
str1 = str1
fmt.Println(str1)
} #以上代码执行结果如下:
hello world Golang! 尹正杰
尹正杰 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;

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
info := make(map[string]int) //定义一个空字典,其key的数据类型是字符串,其value对应的数据是数字
info["age"] = 25 //往空字典中传值
info["size"] = 18
fmt.Println(info["age"]) //查看key所对应的value.
info["age"] = info["size"] + 100 //其实是数字相加运算
fmt.Println(info["age"])
info2 := map[string]string{
"name" : "尹正杰",
"age" : "",
} c,ok := info2["c"] //判断key是否在info2中
if ok {
fmt.Println(c)
}else {
fmt.Println("真不好意思,你所说的key在我的字典里压根不存在!")
}
fmt.Println(info2) if hight,ok := info["d"];ok { //判断key是否在info中
fmt.Println(hight)
}else {
info["hight"] = 111
}
fmt.Println(info)
} #以上代码执行结果如下:
25
118
真不好意思,你所说的key在我的字典里压根不存在!
map[name:尹正杰 age:25]
map[age:118 size:18 hight:111]

2.删除一个map;

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
dict:= map[string]int{
"a" :1,
}
fmt.Println(dict)
delete(dict,"a") //删除map中key所对应的value.
fmt.Println(dict) var dict_1 map[string]int
fmt.Println(dict_1 == nil) //定义了一个空字典,内容为空
fmt.Println(dict_1)
dict_1 = make(map[string]int) //如果m1等于空(nil),需要重新m1才能用m1,不能直接对其赋值
dict_1["c"]=100
fmt.Println(dict_1)
} #以上代码执行结果如下:
map[a:1]
map[]
true
map[]
map[c:100]

3.遍历map;

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
ages := map[string]string{
"姓名":"尹正杰",
"年龄":"",
}
for i,j := range ages { //遍历key和value。
fmt.Println("key=",i,"value=",j)
} for i := range ages { //只遍历key.
fmt.Println(i)
}
} #以上代码执行结果如下:
key= 姓名 value= 尹正杰
key= 年龄 value= 25
姓名
年龄

4.小试牛刀;

A.定义一个集合的思想;

  学过Python的同学,可能听说过集合,但是我要告诉你一个好消息和一个坏消息,你想先听哪一个?

  坏消息就是Golang没有集合这个概念;好消息是我们可以用Golang的make函数来实现集合的思想,下面跟我一起看个例子吧!

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func main() {
set := make(map[string]bool) //定义一个map
set["a"] = true //给已经存在的变量定义为真
if set["a"] {
fmt.Println("已经存在该变量") } else {
fmt.Println("该变量不存在!")
} if set["b"] {
fmt.Println("已经存在该变量") } else {
fmt.Println("该变量不存在!") }
} #以上代码执行结果如下:
已经存在该变量
该变量不存在!

B.统计单词出现的频率;

   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

单词.txt

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import (
"fmt"
"io/ioutil"
"log"
"strings"
) func main() {
buf,err := ioutil.ReadFile("D:\\Golang环境\\Golang Program\\Golang lesson\\Day4\\单词.txt")
if err != nil {
log.Fatal(err)
}
statistic_times := make(map[string]int)
words_length := strings.Fields(string(buf)) for counts,word := range words_length {
word,ok :=statistic_times[word] //判断key是否存在,这个word是字符串,这个counts是统计的word的次数。
if ok{
word = word //我这里是重新赋值,因为上面定义了,下面必须用这个变量,不然就报错,有大神可以帮忙优化一下这里。
statistic_times[words_length[counts]] = statistic_times[words_length[counts]] + 1
}else {
statistic_times[words_length[counts]] = 1
}
}
for word,counts := range statistic_times {
fmt.Println(word,counts)
}
} #以上代码输出结果如下: lines 3
method 1
functions 1
in 1
words 1
may 1
newline-delimited 1
the 6
package 1
provides 1
will 1
step 1
through 1
skipping 1
stripped 1
Scanner 1
as 1
tokens 2
specification 1
space-delimited 1
client 1
function 3
instead 1
for 2
of 5
by 1
termination 1
interface 1
between 1
breaks 1
with 1
line 1
runes 1
Successive 1
SplitFunc 1
into 2
Split 1
reading 1
defined 2
type 1
default 1
input 1
custom 1
text 1
to 1
Scan 1
bytes 2
is 1
such 1
token 1
are 1
file 3
The 2
split 3
UTF-8-encoded 1
and 1
provide 1
a 7
convenient 1
data 1
calls 1
this 1
scanning 1

四.struct(结构体);

  说道结构体,大家可能会懵逼,不知道是个啥东西,其实我觉得Golang起的结构体这个名字还是蛮接地气的,不是吗?从字面意思就可以理解为一个数据的结构体系。基本上一听这个名字就大致知道是干嘛的,它就好似一个模板,让我们看清楚了她的各个主要分支结构。其实,在Python中,我们叫它实例,说白了,当初学习Python实例的和实例化的时候让我很懵逼,随着时间的推移我才明白什么是实例,什么是实例化。相信学习过Python的同学应该都知道class。其实结构体,我们就可以理解成Python中的定义一个实例,而用这个结构体的时候,我们就可以理解是在实例化这个对象。

1.定义一个结构体以及实例化结构体的两种方式;

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" type Student struct { //定义一个结构体,类似与python定义的一个实例
ID int
Name string
} func main() {
var s Student //引用结构体,方式一。【可以理解为实例化】
s.ID = 100
s.Name = "yinzhengjie"
fmt.Println(s) s1 := Student{ //引用结构体,方式二
ID:200,
Name:"饼干",
}
fmt.Println(s1)
} #以上代码输出结果如下:
{100 yinzhengjie}
{200 饼干}

2.结构体的指针;

  在Golang的全局变量中,我们声明一个变量的同时,还需要制定其数据类型,可以是int,string,byte,也可以是[]int,[]string,[]byte,还可以是我们自定义的结构体等等。

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" type Student struct { //定义一个结构体
ID int
Name string
} func main() { s1 := Student{ //引用(实例化)结构体
ID:100,
Name:"尹正杰",
}
fmt.Println(s1) var p *Student //定义p为我们自定义的结构体类型
p = &s1 //将实例化的内存地址传给p
p.ID = 200 //修改结构体里面的ID参数为200,由于p是指针类型,故会修改s1的ID的值。
fmt.Println(s1) var p1 *int
p1 = &s1.ID //我们也可以直接取到我们自定义的结构体重的参数的内存地址,然后给其赋值,也能达到修改参数的效果。
*p1 = 300
fmt.Println(s1)
} #以上代码输出结果如下:
{100 尹正杰}
{200 尹正杰}
{300 尹正杰}

五.交互模式

  一般用Golang写的运维工具都是自己把功能跑完,不需要每次手动执行一些命令才能完成工作。但是在这几道一些安全性信息的时候就不得不要求用户交互了,比如你登录QQ,登录你的爱奇艺会员,腾讯会员以及乐视会员等等。都需要你手动输入一些字符串。那么问题来了,Golang是如何实现这功能的呢?跟着我一起实验吧!

1.初探交互模式;

  我们写一个交互程序,让用户输入什么就打印出来什么。

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" var cmd string func main() {
for { //循环输入
fmt.Print("input>>") //输入的内容都是字符串类型。
fmt.Scan(&cmd) //获取用命令行中第一字符串传给cmd。
if cmd == "stop" { //定义结束循环的关键字
break
}
fmt.Println(cmd) //将输入的字符串cmd变量打印出来
}
} #以上代码执行结果如下:
input>>尹正杰
尹正杰
input>>yinzhengjie
yinzhengjie
input>>您好
您好
input>>hello
hello
input>>world
world
input>>

2.获取一整行内容;

  获取通过第一个交互模式你也体会到了,存在很多坑,比如不能把输出空格或者回车就会卡在那里不动了,无法获取完整的一行内容并打印,只能讲命令行的第一个位置参数给打印出来,那么如果将一整行的内容都打印出来呢?这个时候我们就需要对bufio这个package需要一定的掌握。

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import (
"bufio"
"os"
"fmt"
"strings"
) var (
s string
n int
line string
)
func main() {
f := bufio.NewReader(os.Stdin) //读取输入的内容
for {
fmt.Print("请输入一些字符串>")
line,_ = f.ReadString('\n') //定义一行输入的内容分隔符。
if len(line) == 1 {
continue //如果用户输入的是一个空行就让用户继续输入。
}
line = strings.Replace(line,"\n"," ",-1) //利用string的修改操作,将换行符脱掉。
// 要注意的是它是需要单独占用内存的。其实这行没必要写,因为只需要把下一行的“\n”去掉就好使啦
// 即:fmt.Printf("您输入的是:%s",line),因为我刚刚学的时候踩了个坑,所以在这里记录下。
fmt.Printf("您输入的是:%s\n",line)
fmt.Sscan(line,&s,&n) //将s和n的值传给line,如果不传值的话就
if s == "stop" {
break
}
fmt.Printf("您输入的第一个参数是:·\033[31;1m%v\033[0m·,输入的第二个参数是··\033[31;1m%v\033[0m·.\n",s,n)
}
} #意思代码输出结果如下:
请输入一些字符串>
请输入一些字符串>
请输入一些字符串>GOLANG 123
您输入的是:GOLANG 123
您输入的第一个参数是:·GOLANG·,输入的第二个参数是··123·.
请输入一些字符串>

六.序列化和反序列化(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/

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/
{"ID":1,"Name":"yinzhengjie"}

a.txt执行代码之后的内容

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import (
"encoding/json"
"log"
"fmt"
"os"
) type Student struct {
ID int
Name string
} func main() {
s := Student{
ID:1,
Name:"yinzhengjie",
}
buf,err := json.Marshal(s) //序列化一个结构体,
if err != nil {
log.Fatal("序列化报错是:%s",err)
}
f,err := os.OpenFile("D:\\Golang环境\\Golang Program\\Golang lesson\\Day4\\a.txt",os.O_APPEND|os.O_CREATE|os.O_RDWR,0644)
if err != nil {
log.Fatal(err)
}
f.WriteString(string(buf))
f.WriteString("\n")
f.Close()
fmt.Println("写入成功")
} #以上代码执行结果如下:
写入成功

2.反序列化之前的内容;

  文件“b.txt”内容如下:

{100 yinzhengjie}

“b.txt”文件内容

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import (
"encoding/json"
"log"
"fmt"
"io/ioutil"
) type Student struct {
ID int
Name string
} func main() {
buf,err := ioutil.ReadFile("D:\\Golang环境\\Golang Program\\Golang lesson\\Day4\\b.txt")
if err != nil {
fmt.Println("你愁啥?文件打开错误了!")
return
}
var str Student
err1 := json.Unmarshal(buf,&str)
if err1 != nil {
log.Fatal("反序列化报错啦:%s",err1)
}
fmt.Println(str)
} #以上代码执行结果如下:
{100 yinzhengjie}

3.小试牛刀;

  写一个简单的交互学生管理系统,需要运用的知识点有:map,文件处理,序列化,字符串处理,结构体等等,以下仅是初稿,暂时提供的一个思路。

 [root@yinzhengjie tmp]# more student.go
/*
#!/usr/bin/env gorun
@author :yinzhengjie
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/
EMAIL:y1053419035@qq.com
*/ package main import (
"bufio"
"fmt"
"os"
"log"
"io"
"encoding/json"
"strings"
) var (
cmd string
name string
id int
line string
file_name string
) type Student struct {
ID int
Name string
} func main() {
f := bufio.NewReader(os.Stdin)
for {
fmt.Print(" 请输入>>> ")
line, _ = f.ReadString('\n')
fmt.Sscan(line, &cmd)
if len(line) == 1 {
continue
}
switch cmd {
case "list":
list()
case "add":
add()
case "save":
save()
case "load":
load()
case "stop":
os.Exit(0)
default:
fmt.Println("您输出的命令无效")
}
}
} func list() {
f,err := os.Open("student_info.json") //打开一个文件,如果这个文件不存在的话就会报错。
if err != nil {
log.Fatal(err)
}
r := bufio.NewReader(f) //取出文件的内容
for {
line,err := r.ReadString('\n')
if err == io.EOF{
break
}
fmt.Print(line)
}
f.Close()
} func add() {
fmt.Sscan(line, &cmd, &id, &name)
f, err := os.Open("student_info.json") //打开一个文件,如果这个文件不存在的话就会报错。
if err != nil {
log.Fatal(err)
}
r := bufio.NewReader(f) //取出文件的内容
flag := 0 //定义一个标志位,当输入的ID和name相同时,就将其的值改为1.
for {
line, err := r.ReadString('\n')
if err == io.EOF {
break
}
s := Student{
ID: id,
Name: name,
}
buf, err := json.Marshal(s)
if err != nil {
log.Fatal("序列化报错是:%s", err)
}
line = strings.Replace(line,"\n","",-1) //将换行符替换为空,你可以理解是删除了换行符。
if line == string(buf) {
fmt.Println("对不起,您输入的用户或者ID已经存在了,请重新输入!")
flag = 1
break }
}
if flag == 0 {
s := Student{
ID:id,
Name:name,
}
buf,err := json.Marshal(s) //序列化一个结构体,
if err != nil {
log.Fatal("序列化报错是:%s",err)
}
fmt.Println(string(buf))
f,err := os.OpenFile("student_info.json",os.O_APPEND|os.O_CREATE|os.O_RDWR,0644)
if err != nil {
log.Fatal(err)
}
f.WriteString(string(buf))
f.WriteString("\n")
f.Close()
fmt.Println("写入成功") }
}
func save() {
fmt.Sscan(line, &cmd,&file_name)
f,err := os.Open("student_info.json") //打开一个文件,如果这个文件不存在的话就会报错。
if err != nil {
log.Fatal(err)
}
r := bufio.NewReader(f) //取出文件的内容
f2,err := os.OpenFile(file_name,os.O_APPEND|os.O_CREATE|os.O_RDWR,0644) //打开一个新文件
if err != nil {
log.Fatal(err)
}
for {
line,err := r.ReadString('\n')
if err == io.EOF{
break
}
f2.WriteString(line) //将student_info.json内容写到指定的文件中去。
}
f2.Close()
f.Close()
} func load() {
fmt.Sscan(line, &cmd,&file_name)
f,err := os.Open(file_name) //打开一个文件,如果这个文件不存在的话就会报错。
if err != nil {
fmt.Println("对不起!系统没用找到该文件!")
return
}
r := bufio.NewReader(f) //取出文件的内容
for {
line,err := r.ReadString('\n')
if err == io.EOF{
break
}
fmt.Print(line)
}
f.Close()
}
[root@yinzhengjie tmp]#
[root@yinzhengjie tmp]#
[root@yinzhengjie tmp]#
[root@yinzhengjie tmp]#
[root@yinzhengjie tmp]# go run student.go
请输入>>>
请输入>>>
请输入>>> list
{"ID":1,"Name":"bingan"}
{"ID":2,"Name":"yinzhengjie"}
{"ID":3,"Name":"jie"}
{"ID":5,"Name":"Golang"}
{"ID":4,"Name":"reboot"}
{"ID":10,"Name":"jie"}
{"ID":100,"Name":"dev"}
{"ID":200,"Name":"jay"}
请输入>>>
请输入>>>
请输入>>> add 300 GOOD
{"ID":300,"Name":"GOOD"}
写入成功
请输入>>> list
{"ID":1,"Name":"bingan"}
{"ID":2,"Name":"yinzhengjie"}
{"ID":3,"Name":"jie"}
{"ID":5,"Name":"Golang"}
{"ID":4,"Name":"reboot"}
{"ID":10,"Name":"jie"}
{"ID":100,"Name":"dev"}
{"ID":200,"Name":"jay"}
{"ID":300,"Name":"GOOD"}
请输入>>>
请输入>>> save 6666
请输入>>>
请输入>>> load 6666
{"ID":1,"Name":"bingan"}
{"ID":2,"Name":"yinzhengjie"}
{"ID":3,"Name":"jie"}
{"ID":5,"Name":"Golang"}
{"ID":4,"Name":"reboot"}
{"ID":10,"Name":"jie"}
{"ID":100,"Name":"dev"}
{"ID":200,"Name":"jay"}
{"ID":300,"Name":"GOOD"}
请输入>>>
请输入>>> stop
You have new mail in /var/spool/mail/root
[root@yinzhengjie tmp]#