golangBasic10-20

10错误处理

错误处理方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import (
"io/ioutil"
"fmt"
)
func main(){
f, err := ioutil.ReadFile("test.txt")
if err != nil{
fmt.Println(err)
}else {
fmt.Println(f)
}
}

自定义错误

Go语言的error类型实际上是一个接口,定义如下:
type error interface {
Error() string
}
error接口包含Error方法,用来返回一个字符串。换言之,所有符合 Error()string 格式的方法,都能实现错误接口。

1
2
3
4
5
6
7
8
9
10
11
12
package main
import (
"fmt"
"errors"
)
func main(){
err := errors.New("this is an error")
var err2 error
fmt.Println(err.Error())
fmt.Println(err2)
}

宕机与恢复

一般而言,只有当程序发生不可逆的错误时,才会使用panic方法来触发宕机。panic方法是Go语言的一个内置函数,使用panic方法后,程序的执行将直接中断。
panic方法的源代码如下,由于其参数为空接口类型,因此我们可以传入任意类型的值作为宕机内容:
func panic(v interface{})

1
2
3
4
5
6
7
package main
import "fmt"
func main(){
panic("Serious bug")
fmt.Println("Invalid code") //程序退出,无法执行该行代码
}

Go语言通过内置函数recover来捕获宕机,类似于其它编程语言中的try-catch机制。
在使用panic方法触发宕机后且在退出当前函数前会调用延迟执行语句defer,代码示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import "fmt"
func protect(){
defer func() { //protect函数退出前执行defer语句
fmt.Println("func protect exit")
}()
panic("Serious bug") //触发宕机
}

func main(){
defer func() { //protect函数退出前执行defer语句
fmt.Println("func main exit")
}()
protect()
fmt.Println("Invalid code")
}

Go语言错误应用

panic()和recover() 虽然能模拟其他语言的异常机制,但是并不建议在Go语言编程中使用类似的方法。正如本章开篇所提到的,推荐使用多值返回来处理错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main
import (
"fmt"
"errors"
)
func div(dividend int, divisor int) (int, error) {
if divisor == 0{ //除数为0则返回错误
return 0,errors.New("divisor is zero")
}
return dividend/divisor,nil
}

func main(){
res1,err:= div(4,2);if err != nil {
fmt.Println(err.Error())
}else { fmt.Println("4/2 =",res1) }
res2,err := div(1,0);if err != nil{
fmt.Println(err.Error())
}else { fmt.Println("1/0 =",res2) }
}

11文件操作

目录基本操作

文件是以硬盘为载体的信息存储集合,文件可以是文本、图片、程序。
系统为了更好的管理文件,便使用了目录。目录是存放文件的地方,为树形层次结构。
想要读取一个目录的内容,可以使用”io/ioutil”库中的ReadDir方法,此方法返回一个有序列表。
对于创建目录可以使用“os“库的如下接口Mkdir。

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
package main
import (
"io/ioutil"
"fmt"
)
func main() {
dir, err := ioutil.ReadDir("D:\\360")
if err != nil {
fmt.Println(err)
}
for _, file := range dir {
fmt.Println(file.Name())
}
}

package main
import (
"os"
"fmt"
)
func createDir(path string, dirName string) {
dirPath := path + "\\" + dirName
err := os.Mkdir(dirPath, 0777)
if err != nil {
fmt.Println(err)
}
os.Chmod(dirPath, 0777)
fmt.Println("Create Dir => " + path + dirName)
}
func main() {
createDir("D:\\360","test")

}

文件基本操作

对于文件的创建与打开使用的是标准库“os“中的OpenFile:
func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
想要读取文件可使用“os”库中的Read接口:
func (f *File) Read(b []byte) (n int, err error)
比之前的文件读取,向文件写内容可用Write:
func (f *File) Write(b []byte) (n int, err error)
删除文件使用的“os”库中的Remove:
func Remove(name string) error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func ReadFile(path string) {
file, err := os.Open(path)
if err != nil {
fmt.Println(err)
}
buf := make([]byte, 1024)
fmt.Println("以下是文件内容:")
for {
//Read函数会改变文件当前偏移量
len, _ := file.Read(buf)
//读取字节数为0时跳出循环
if len == 0 {
break
}
fmt.Println(string(buf))
}
file.Close()
}

处理json文件

JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。JSON最初是属于JavaScript的一部分,后来由于具有良好的可读和便于快速编写的特性,它现在已独立于语言

标准库提供了"encoding/json"库来处理JSON。编码JSON,即从其他的数据类型编码成JSON字符串,这个过程,我们会使用如下的接口:
func Marshal(v interface{}) ([]byte, error)
解码JSON会使用到Unmarshal接口,也就是Marshal的反操作。
func Unmarshal(data []byte, v interface{}) error

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
36
37
38
39
40
41
42
43
44
func main(){
jsonStr :=`
{
"name": "小王",
"age": 24,
"sex": true,
"birthday": "1995-01-01",
"company": "喵喵公司",
"language" :[
"Go",
"PHP",
"Python"
]
}`
m :=make(map[string]interface{},6)
err := json.Unmarshal([]byte(jsonStr),&m)
if err != nil {
fmt.Println(err)
}
fmt.Println("m = ",m)
//类型断言
for key, value := range m{
switch data :=value.(type) {
case string:
fmt.Printf("map[%s]的值类型为string,value = %s\n",key,data)
case float64:
fmt.Printf("map[%s]的值类型为int,value = %f\n",key,data)
case bool:
fmt.Printf("map[%s]的值类型为bool,value = %t\n",key,data)
case []string:
fmt.Printf("map[%s]的值类型为[]string,value = %v\n",key,data)
case []interface{}:
fmt.Printf("map[%s]的值类型为[]interface,value = %v\n",key,data)
}
}
}
执行结果:
m = map[age:24 birthday:1995-01-01 company:喵喵公司 language:[Go PHP Python] name:小王 sex:true]
map[name]的值类型为string,value = 小王
map[age]的值类型为int,value = 24.000000
map[sex]的值类型为bool,value = true
map[birthday]的值类型为string,value = 1995-01-01
map[company]的值类型为string,value = 喵喵公司
map[language]的值类型为[]interface,value = [Go PHP Python]

12接口类型

接口定义

在Go语言中,接口是一个自定义类型,接口类型是一种抽象的类型,它不会暴露出他所代表的内部值的结构,它只会展示出它自己的方法,因此,接口类型不能将其实例化。
Go语言的接口是非常灵活的,它通过一种方式来声明对象的行为,谁实现了这些行为,就相当于实现了这个接口里面声明各种方法的集合,但接口本身不去实现这些方法所要的一些操作

1
2
3
type InterfaceName interface{
Method()
}

接口创建

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
type Mysql struct {
DBName string
isConnect bool
}
func (mysql *Mysql)Connect() error {
fmt.Println("Mysql Connect DB => "+ mysql.DBName)
mysql.isConnect = true
if mysql.isConnect {
fmt.Println("Mysql Connect Success!")
return nil
} else {
return errors.New("Connect failure! ")
}
}

func (mysql *Mysql)Disconnect() error {
fmt.Println("Mysql Disconnect Success!")
return nil
}

func main(){
var mysql =Mysql{DBName: "student"}
fmt.Println("开始连接")
mysql.Connect()
fmt.Println("断开连接")
mysql.Disconnect()
}
执行结果:
开始连接
Mysql Connect DB => student
Mysql Connect Success!
断开连接
Mysql Disconnect Success!

接口赋值存在2种情况:

1将对象实例赋值给接口

只能是对象指针赋值,而不是对象直接赋值,否者会发生错误

2将接口赋值给另一接口

a两接口拥有同样方法集,可相互赋值

b若A是B的子集,则B可赋值给A,A不可赋值给B

接口嵌入(接口组合)

类似结构体的内嵌,是继承特性的实现,体现了非侵入式的风格

接口不能嵌入自身,包括直接嵌入和间接嵌入

空接口

空接口(interface{})是Go语言中最特殊的接口,在Java语言中,所有的类都继承自一个基类Object,而Go中的interface{}接口就相当于Java语言里的Object。
在Go语言中,空接口不包含任何方法,也正因如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值。

*空接口不能直接赋值给变量,需要类型断言。

空接口常见使用方式

1
func Println(a ...interface{})(n int, err error)"..."表示可变长参数

类型断言

类型断言是使用在接口变量上的操作。简单来说,接口类型向普通类型的转换就是类型断言。
类型断言的语法是:

1
t, ok := X.(T)//判断X的类型是否为T断言成功,则ok为true,t的值为接口变量X的动态值;断言失败则,则t的值为类型T的初始值,t的类型始终为T。例如:var X interface{}=1//空接口存储变量t0,ok=X.(string)断言失败,0为tring的初始值,即空字符串“”,t类型为string若t0,ok=X.(int)则断言成功,t0为X动态值X,t的类型为int,ok=true

类型断言有2中情况:

1断言类型T是一个具体的类型

2断言类型T是一个接口类型

接口类型断言有2种方式:

1ok-pattern

2switch-type//要断言的接口类型种类较多时使用

1
switch value := 接口变量.(type){    case 类型1:        //类型1时的处理    case 类型2:        //类型2时的处理    ...    default:       //默认时的处理}

侵入式接口与非侵入式接口

侵入式接口:需要显示的创建一个类去实现一个接口(大部分语言)

非侵入式接口:与上相反,且接口设计更简洁、灵活,更注重实用性

非侵入式的3大好处:。。。

13并发与通道

并发编程包括多线程编程、多进程编程以及分布式程序,本章所所讲述的并发叫做协程属于多线程编程

调用栈:经常被用于存放子程序的返回地址

CSP:go语言的并发基于CSP(communication Sequential Process,通讯顺序进程)模型,该模型提倡通过通信来共享内存,而非通过共享内存来通信。go基于CSP,意味着显示锁都是可以避免的

gorountine

并行(Parallelism):指在同一时刻,有多条指令在多个处理器上同时执行。

并发(Concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行,只是把时间片分成了若干段,使的多个进程快速交替的执行。

只需要在函数调用语句前面添加go关键字,就可以创建并发执行单元。开发人员无需了解任何执行细节,调度器会自动将其安排到合适的系统线程上去执行。

gorountine是并发的核心,也叫协程,go内部已实现gorountine之间的内存共享,它比线程更加易用、高效和轻便。

go语言中,每个并发执行单元叫作一个gorountine,在函数前加上go关键字,就能使这个函数以协程的方式运行

1
go 函数名(函数参数)

一旦使用go,就不能使用函数返回值来与主进程进行数据交换,而只能使用channel

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
func Tesk1(){
for {
fmt.Println(time.Now().Format("15:04:05"),"正在处理Task1的任务!")
time.Sleep(time.Second *3)
}
}
func Tesk2(){
for {
fmt.Println(time.Now().Format("15:04:05"),"正在处理Task2的任务!")
time.Sleep(time.Second *1)
}
}

func main(){
go Tesk1()
go Tesk2()
for {
fmt.Println(time.Now().Format("15:04:05"),"正在处理主进程的任务!")
time.Sleep(time.Second *2)
}
执行结果:
22:03:50 正在处理主进程的任务!
22:03:50 正在处理Task1的任务!
22:03:50 正在处理Task2的任务!
22:03:51 正在处理Task2的任务!
22:03:52 正在处理主进程的任务!
22:03:52 正在处理Task2的任务!
22:03:53 正在处理Task1的任务!
22:03:53 正在处理Task2的任务!
22:03:54 正在处理主进程的任务!
22:03:54 正在处理Task2的任务!

所有gorountine函数在main函数结束时会一并结束。虽然gorountine类似于线程概念,但调度上不如线程细腻,而细腻程度取决于gorountine调度器的实现和运行环境。终止gorountine最好的方法是直接在函数中自然返回。

go关键字后也可以是匿名函数

1
2
3
go func(参数列表){
函数体
} (调用参数列表)

runtime

runtime包实现了一个小型的任务调度器。

三大函数

1Gosched()

使当前Go协程放弃处理器,以让其他Go协程运行。它不会挂起当前协程,因此当前Go协程会恢复执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main(){
go func() {
for i := 0;i < 3;i++{
fmt.Println("go")
}
}()
for i :=0;i<2;i++{
runtime.Gosched()//阻止获得CPU控制权 运行完协程后,继续进行
fmt.Println("main")
}
}
执行结果:
go
go
go
main
main

Go语言的协程是抢占式调度,当一下几种情况,会主动将CPU转让出去:

*syscall

*C函数调用(本质和syscall一样)

*主动调用runtime.1Gosched

*某个gorountine的调用时间超过100ms,并且这个gorountine调用了非内联的函数

2Goexit()

终止调用它的Go协程,但其他Go协程不会受影响,效果和return一样

3GOMAXPROCS()

GOMAXPROCS(n int)函数可以设置程序在运行中所使用的CPU数。

可用NumCPU查询本机器逻辑CPU数

channel

goroutine运行在相同的地址空间,因此访问共享内存必须做好同步。引用类型channel是CSP模式的具体体现,用于多个goroutine之间的通讯。其内部实现了同步,确保并发安全。

channel是一种特殊的类型,和map类似,channel也是一个对应make创建的底层数据结构的引用。声明一个channel的方式如下:

1
var 通道变量 chan 通道类型通道变量:是保存通道的引用变量通道类型:指该通道可传输的数据类型。

和其他引用类型一样,channel的零值也是nil

make创建channel

1
make(chan Type)//等价于make(chan Type,0)make(chan Type, capacity)capacity为0时,channel无缓冲阻塞读写,大于0时,是有缓冲非阻塞的,直到写满capacity个元素彩阻塞写入。默认情况下,channel接收和发送数据都是阻塞的,除非另一端已准备好接收,这样goroutine的同步更加简单,而不需要显式锁channel<-value //发送value到channel<-channel      //接收并将其丢弃x :=<-channel  // 从channel中接收数据,并赋值给xx , ok :=<-channel //同上,并检测通道是否关闭,将此状态赋值给ok

缓冲机制:

无缓冲通道,接收前没有能力保存任何值的通道

1
make(chan Type)//等价于make(chan Type,0)需要发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作。若两个goroutine没同时准备好,则导致先执行发送或接收的goroutine阻塞等待例如:func main(){	ch :=make(chan int,0)	go func() {		for i :=0;i<3;i++ {			fmt.Printf("len(ch)=%v,cap(ch)=%v\n",len(ch),cap(ch))			ch <- i  //发送		}	}()	for i :=0;i<3;i++{		time.Sleep(time.Second)  //沉默1s 协程运行		fmt.Println(<-ch)//接收	}}执行结果len(ch)=0,cap(ch)=0 //无缓冲通道 打印通道len和cap,之后发送-阻塞0                   //无缓冲通道 接收-打印通道-阻塞len(ch)=0,cap(ch)=01len(ch)=0,cap(ch)=02无缓冲的通道,保证进行发送和接收的goroutine会在同一时间进行数据交换。有缓冲的通道,无法保证

有缓冲通道,

1
make(chan Type, capacity)存在缓存区,在缓存区为填满的情况下,程序不会被阻塞执行

close和range

close关闭channel()需注意:

1
channel不像文件需要经常关闭,只有确实没有任何需要发送的数据时,或者想要显示地结束range循环等时,才会去关闭channel关闭channel后,无法向channel再次发送数据,再次发送将引发panic错误关闭channel后,可以继续从channel接收数据对于nil channel,无论接收还是发送都会被阻塞

range遍历channel

1
for data :=range ch {}当channel关闭后,range也能自动结束本次遍历

单向channel

1
var ch1 chan int //ch1为一个双向通道var ch2 chan<- int //ch2为一个只能接收的单向通道var <-chan int //ch3为一个只能发送的单向通道普通双向channel能隐式的转换为单向channel,但单向channel不能转为双向channel

定时器

定时器的实现就是使用了单向channel

select

Go语言提供一个关键字select,通过select可以监听channel上的数据流动,select的用法和switch非常相似,由select开始一个新的选择块,每个选择条件有case语句来描述。

与switch语句可以选择任何可使用相比较的条件相比,select有较多的限制,其中最大一条限制就是每个case语句里面必须是一个IO操作,大致结构如下:

1
select {    case <-chan1:	  // 如果chan1成功读到数据,则执行该case语句    case chan2 <- 1:	  // 如果成功向chan2写入数据,则执行该case语句    default:	  //如果上面的case都没有执行成功,则执行该default语句	}

沉默和阻塞(100ms&&调用了非内联函数)都对导致channel失去CPU控制权

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
func main(){
ch :=make(chan int)
go func() {
for i :=0;i<3;i++{
ch<- i//2并阻塞
fmt.Println("通道")//6
}
}()
for {
select {//4
case msg := <-ch://3
fmt.Println(msg)
default://1 5
fmt.Println("默认")
time.Sleep(time.Second)
}
}
}

执行结果:
默认
0
默认
通道
1
默认
通道
通道
2
默认
默认
默认

select实现阻塞超时机制

1
2
3
4
5
6
done :=make(chan bool)
...
case <-time.After(time.Second *3):
fmt.Println("已超时3秒")
done <-true
...

死锁:非缓冲信道上若发生了流入无流出,或者流出无流入,都会导致死锁

1
2
3
4
func main(){
ch :=make(chan int)
<- ch //阻塞main goroutine,信道ch被锁
}

14反射

fmt.Println(),fmt包使用了reflect的反射标准库

反射的强大之处在于它非常灵活,但同时也带来了弊端,比如代码的可读性和可维护性变差,性能也大大折扣

reflect.TypeOf() 获取变量的值类型

1
var typeOfNum reflect.type=reflect.TypeOf(num)typeameOfNum().Name() 获取类型的名称typeameOfNum().Kind() 获取类型的种类typeameOfNum().Elem() 对于指针类型变量,获取指针指向的元素类型获取结构体成员类型typeameOfNum().NumField()获取结构体成员数量typeameOfNum().Field()根据索引返回对应结构体字段的详细信息typeameOfNum().FieldByName(name string)查找字段名来获取字段信息typeameOfNum().FieldByIndex(index []int)通过索引获取字段信息

reflect.ValueOf() 获取变量的原始值

1
var valueOfNum reflect.Value =reflect.ValueOf(num)valueOfNum.Int()用于获取int类型变量的值,若用于获取其他Kind的值,将会引发panic获取结构体成员字段的值valueOfNum.Field()通过索引返回对应结构体字段的valueOfNum反射值类型
1
2
3
4
5
6
7
8
9
10
11
func main(){    
var a interface{}="我是字符串"
typeOfa :=reflect.TypeOf(a)
fmt.Println("变量a的类型为:" +typeOfa.Name())
valueOfa :=reflect.ValueOf(a) //类型的种类==string
if typeOfa.Kind() == reflect.String { //值的字符串
fmt.Println("a变量的值为:" +valueOfa.String())
}
}
执行结果:
变量a的类型为:stringa变量的值为:我是字符串Go语言不支持解析string,反射机制稚嫩和作用于已经存在的对象上

反射三定律

1反射可以将接口类型变量 转换为反射类型变量

2反射可以将反射类型变量 转换为接口类型变量

3想要使用反射来修改变量的值,其值必须是可写的(CanSet)。这个值需满足两个条件一是可以被寻址(CanAddr),二是变量可导出(接固体字段首字母大写)

反射的性能

反射的性能的性能极差,在考虑性能的地方能不用反射就不用

15go命令行工具

相较于Java和C++的编译速度,Go的快速编译是一个主要的效率优势

编译相关指令

build

go build(无参编译)
如果我们在执行go build命令时不后跟任何代码包,那么命令将试图编译当前目录所对应的代码包。
go build + 文件列表
使用这种方式编译时,作为参数的多个Go源码文件必须在同一个目录中。
go build + 包
go build+包的方式编译需要将编译的包放到GOPATH下,否则编译器会找不到你想要编译的包。

减小体积。。。。

源码分三类:

1命令源码文件2库源码文件3测试源码文件

run

go run 该命令会编译执行Go源码文件,run的对象只能是单个或多个.go文件

(必须同属于一个main包),且不能为测试文件,无法针对包运行这个命令

1
2
-work 显示当前的编译目录
-n 输出编译过程时所用到的命令

install

go install用于编译后安装,该命令依赖于GOPATH,是将编译的中间件放到pkg目录下,编译出的可执行文件放到bin目录下

交叉编译

在一个平台上生成另一个平台上的可执行代码,使同一个体系结构可以运行不同的操作系统。

Go的交叉编译很方便,只需要编译前设置Go环境变量CGO_ENABLED/GOOS/GOARCH即可,在1.5以后得版本中,Go语言编译器使用Go语言编写,而不再使用C语言

代码获取(get)

go get用于远程仓库中下载安装远程代码包

1
go get github.com/gin-gonic/gin

-n查看运行过程中使用的命令,可以发现go get其实就是git clone下载源码,之后再对源码进行编译安装,使用时,可能会因网络问题下载失败,这时可以给git添加代理,加速下载

1
2
http代理:。。。
socket5代理:。。。

格式化代码

gofmt +文件路径 格式化这个文件

gofmt +目录 格式化这个目录所有.go文件

gofmt 格式化当前目录下的所有.go文件

注释文档

go doc

Gp语言文档只需要 在每个函数上方 用注释的方式 介绍该函数的作用及使用方法 go fmt命令就会自动将这些注释转化为文档展示出来。

gofmt命令有个非常重要的参数-http,作用是开启Web服务,提供交互式的文档查看页面

1
godoc-http :8080

运行后,浏览器访问http:127.0.0.1:8080/即可以网页的方式查看Go语言文档

代码测试

go test命令用于对Go语言编写的代码包进行测试。可以指定要测试的文件,也可以直接对整个包进行测试,但需要包中含有测试文件,测试文件都是以“_test.go”结尾,其中的函数名以“Test”为前缀,需传入*testing.T类型的参数。

单元测试:目的是发现产品代码是否存在功能性问题、缺陷、以及是否符合预期的运行。结果要么PASS要么FALL,需用到testing测试框架

1
2
3
4
5
6
7
8
9
func Add(a, b int) int{    
return a+b
}
func TestAdd(t *testing.T){
if Add(1,3) != 4{
t.Error("Add函数存在问题!")
}
}
>go test XXXX_test.go -v-v 表示输出详细信息

基准测试

可提供自定义的计时器和一套基准测试算法,能方便快速的分析一段代码可能存在的CPU性能和分配问题

书写规范:

1
2
3
4
5
"Benchmark"为前缀,后加测试函数名,使用“*testing.B”作函数参数
func BenchmarkFunc(b *testing.B){
b.ResetTimer() XXXXXXX//测试代码
b.StopTimer()
}

覆盖率测试

用于统计通过运行程序包的测试有多少代码得到执行,使用参数-cover

1
go test -cover xxx.go

性能分析

Go语言支持使用go tool pprof工具 进行性能查看和调优,使用前序安装其依赖的图形绘制工具Graphviz

1通过文件方式:

输出CPU性能参数到文件cpu_file.prof,并对文件进行分析

1
>go tool pprof cpu_file.prof

通过http方式:

2需引入包

1
import - "net/http/pprof"

16正则表达

正则表达式介绍

正则表达式顾名思义:符合一定规则的表达式,就是用于匹配字符串中字符组合的模式。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本(字符串)

正则表达式语法

普通字符
字符转义
元字符
限定符
定位符
分组构造
匹配模式

regexp包

MarchString函数接收一个要查找的正则表达式和目标字符串,并根据匹配结果返回true或flase,函数定义如下:

1
func MatchString(pattern string, s string) (matched bool, err error)

对于目标字符串:“hello world”,我们通过MatchString函数匹配其中的“hello”字符串,并返回匹配结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import (
"regexp"
"fmt"
)
func main() {
targetString := "hello world"
matchString := "hello"
match , err := regexp.MatchString(matchString,targetString)
if err != nil{
fmt.Println(err)
}
fmt.Println(match)
}

FindStringIndex()/Compile()/MustCompile()

ReplaceAllString()

常用正则表达式参考。。。。。

17/18http编程

http简介

超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。它详细规定了浏览器和万维网服务器之间的相互通信规则,所有的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法

我们在浏览网页时,从打开浏览器、输入网址到按下回车后网页上出现内容的过程中,浏览器到底做了哪些操作呢?

http客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import (
"net/http"
"fmt"
)
func main() {
client := &http.Client{}
request, err := http.NewRequest("GET", "http://www.baidu.com", nil)
if err != nil {
fmt.Println(err)
}
response, err := client.Do(request)
fmt.Println(response.StatusCode)
}

colly框架

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
36
package main

import (
"fmt"
"github.com/gocolly/colly"
)
func main() {
//初始化
c :=colly.NewCollector(
colly.UserAgent("Mozilla/5.0 (Windows NT 10.0;WOW64)" +
" AppleWebKit/537.36 (KHTML,like Gecko) Chrome/72.0.3626.121 Safari/537.36"))
c.OnRequest(func(r *colly.Request) {
r.Headers.Set("Connection","keep-alive")
r.Headers.Set("Accept","*/*")
r.Headers.Set("Origin","")
r.Headers.Set("Accept-Encoding","gzip, deflate")
r.Headers.Set("Accept-Language","zh-CN,zh;q=0.9")
fmt.Println("Visiting",r.URL)
})
c.OnResponse(func(r *colly.Response) {
fmt.Println("response received",r.StatusCode)
})
c.OnHTML("title",func(e *colly.HTMLElement) {
fmt.Println("title:",e.Text)
})
c.Visit("https://www.baidu.com")
}

命令
>go build
>./awesomeProject1

执行结果
Visiting https://www.baidu.com
response received 200
title: 百度一下,你就知道

gin框架

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
项目名:awesomeProject1

package main

import (
//引入gin包
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
//初始化一个gin.Engine对象
router :=gin.Default()
router.GET("/user/:name", func(c *gin.Context) {
name :=c.Param("name")
c.String(http.StatusOK,"hello %s",name)

})
router.GET("/user/:name/*action", func(c *gin.Context) {
name :=c.Param("name")
action :=c.Param("action")
message :=c.Request.Method + " => " +name + " is " + action
c.String(http.StatusOK,message)
})
router.POST("/user/:name/*action", func(c *gin.Context) {
name :=c.Param("name")
action :=c.Param("action")
message :=c.Request.Method + " => " +name + " is " + action
c.String(http.StatusOK,message)
})

router.Run(":9090")
}

命令行:

1
2
>go build
>./awesomeProject1

执行结果

1
2
3
4
5
6
7
8
浏览器
http://127.0.0.1:9090/user/john
hello john
http://127.0.0.1:9090/user/john/send
GET => john is /send

postman-post
POST => john is /send

19Socket编程

OSI七层模型

OSI是一个开放性的通信系统互连参考模型
OSI的7层从上到下分别是 7 应用层 6 表示层 5 会话层 4 传输层 3 网络层 2 数据链路层 1 物理层

应用层
表示层
会话层
传输层
网络层
数据链路层
物理层

TCP/IP模型是一个网络通信模型,以及一整个网络传输协议家族,为互联网的基础通信架构,常被视为是简化的七层OSI模型

OSI七层模型 TCP/IP模型
应用层 应用层
表示层
会话层
传输层 传输层
网络层 网络层
数据链路层 网络访问层
物理层

SOCKET基础

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket。建立网络通信连接至少要一对端口号(Socket)。Socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口

TCP编程

TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义
TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后,等待对方回答SYN+ACK,并最终对对方的 SYN 执行 ACK 确认。这种建立连接的方法可以防止产生错误的连接,TCP使用的流量控制协议是可变大小的滑动窗口协议。

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
package main
import (
"net"
"log"
)
func main() {
// 监听8080端口
l, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal("服务启动失败!", err)
}
defer l.Close()
log.Println("服务启动成功!")
}

package main
import (
"net"
"log"
)
func main() {
// 尝试连接百度服务器
conn, err := net.Dial("tcp", "www.baidu.com:80")
if err != nil {
log.Fatal("连接失败!", err)
}
defer conn.Close()
log.Println("连接成功!")
}

20数据库编程

mysql简介

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一

MySQL有非常多的分支版本,官方版本(目前由Oracle维护)的下载地址为:
https://www.mysql.com/downloads/

安装完MySQL后,可以以使用如下格式连接MySQL服务器,需要注意的是-p后面与密码不能有空格。

mysql -h主机地址 –u 用户名 -p用户密码

数据库基本操作

数据库的最基本的操作便是增(create)、删(delete)、改(update)、查(read),简称CURD。数据库中有8类对象,分别是数据库、数据表、记录、字段、索引、查询、过滤器、视图,

Go语言中sql包提供了一个Open方法来创建一个数据库连接。
func Open(driverName, dataSourceName string) (*DB, error)
使用Go语言进行创建数据表需要使用Exec函数。
func (db *DB) Exec(query string, args …interface{}) (Result, error)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main() {
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/01kuaixue")
checkErr(err)
defer db.Close()
// 验证连接的可用性
err = db.Ping()
checkErr(err)
log.Println("数据库连接成功!")
stmt, err := db.Prepare("INSERT INTO `user`(username,gender,password,created) VALUES (?,?,?,?)")
defer stmt.Close()
rs, err := stmt.Exec("Ailsa",0,"111111",time.Now())
checkErr(err)
rowCount, err := rs.RowsAffected()
checkErr(err)
log.Printf("插入了 %d 行", rowCount)
}

MySQL 数据库中事务是用户一系列的数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位。
事务具有 4 个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持续性(Durability)。这 4 个特性简称为 ACID 原则。