Featured image of post 算法概述

算法概述

本文阅读量

基础

数据类型

整型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
uint8   0-255
uint16  0-65535
uint32
uint64 

int8  -128 -  127
int16  -32768 - 32767
int32 
int64

uint 32位 uint32  64位 uint64
int 32位 int32  64位 int64

浮点

1
2
float32 最大值math.MaxFloat32
float64 最大值math.MaxFloat64

复数

1
2
complex64   实部和虚部为32位
complex128  实部和虚部为64位

数组

 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
# 声明一个含有3个int值的数组
var a [3]int
# 声明并赋值
var b = [3]int{1,2}
# 动态扩容数组长度(由元素决定)
var c = [...]int{1,2}
# 数组指定索引
var d = [...]int{1:1,3:5}

# 多维数组
e := [3][2]string{
	{"北京", "上海"},
    {"广州", "深圳"},
    {"成都", "重庆"},
}

# 让编译器推导数组长度
f := [...][2]string{
	{"北京", "上海"},
    {"广州", "深圳"},
    {"成都", "重庆"},
}

# 获取数组长度
l:=len(f)

切片

 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
# 声明一个切片
var a []string

# 声明一个切片并初始化
var b = []int{}


# 截取数组生成切片
a:=[5]int{1,2,3,4,5}
c := a[1:3] # 索引范围为:[1,3)
# make构建切片(已经初始化了)
d:=make([]int,2,10) # 2个元素,容量为10的int切片

# 给切片添加元素
e = append(d,make([]int,1,3))
f = append(e,3,4,5)
#可以不初始化,直接添加元素
var s []int
s = append(s,1,2,3)

# 切片复制
g := []int{1, 2, 3, 4, 5}
h := make([]int, 5, 5)
copy(h, g)   #使用copy()函数将切片g中的元素复制到切片h

# 删除元素
i := []int{30, 31, 32, 33, 34, 35, 36, 37}
# 要删除索引为2的元素
j = append(i[:2], i[3:]...) # [30 31 33 34 35 36 37]

# 获取切片容量
h := cap(j)

map

 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
# 生成容量为8的map
a := make(map[string]int,8)
a['zs'] = 18
a['ls'] = 19

# 声明并初始化值
b := map[string]int{
	'zs':18,
	'ls': 19,
}

# 判断key是否存在
v,ok := b['zs'] #存在 ok=>true

# 删除key
delete(b,'zs')

# 元素为map类型的切片
var c = make([]map[string]string,3)
# 对map元素初始化
c[0]["name"] = "snailsir"
c[0]["pwd"] = "123456"

# 值为切片的map
var d = make(map[string][]string,3)
# 赋值时切片也要初始化
sv := make([]string,0,2)
d['tag'] = sv

函数

 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
45
46
47
48
49
50
51
52
53
54
55
56
57
# 声明含有2个int参数的函数
func intSum(x,y int) int{}

# 定义一个含所有可变参数的int类型的函数
func intSum2(x ...int) int{} #x是一个切片

#含有固定参数与可变参数的函数
func intSum3(x int,y ...int) (int,int){}

# 定义一个函数类型
type cal func(int,int) int
var add cal
add = intSum
fmt.PrintLn(add(1,2))

# 函数作为参数
func calc(x,y int,opt func(int,int) int) int{
	return opt(x,y)
}

#函数作为返回值
func do(s string) (func(int,int) int,error) {
	return inSum,nil
}

# 匿名函数
add := func(x,y int){
	fmt.Println(x + y)
}
add(10,20)

# 闭包
#指:一个函数和与其相关的引用环境组合而成的实体
func adder() func(int) int {
	var x int
	return func(y int) int {
		x += y
		return x
	}
}

func main(){
	var f = adder()
	fmt.Println(f(10)) //10
}

# panic 与 recover
func main() {
    defer func() {
        err := recover()
        //如果程序出出现了panic错误,可以通过recover恢复过来
        if err != nil {
            fmt.Println("recover in B")
        }
        }()
    panic("panic in B")
}

内置函数

内置函数 介绍
close 主要用来关闭channel
len 用来求长度,比如string、array、slice、map、channel
new 用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针
make 用来分配内存,主要用来分配引用类型,比如chan、map、slice
append 用来追加元素到数组、slice中
panic和recover 用来做错误处理

指针

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 取指针
a:=10
b:=&a

# 根据指针取值
c := *b

# new得到的是一个类型的指针
# new一个类型的指针
func main(){
	var a *int
	a = new(int) #该类型零值
	*a = 10
	fmt.Println(*a)
}

# make只用于slice,map已经chan的内存创建

结构体

 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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#结构体
type user struct{
	name string
	age byte
}
var userInfo = [...]user{
	{"Tom",20},
	{"Mary",18},
}


# 匿名结构体
var user struct{Name string; Age int}
user.Name = "snailsir"
user.Age = 18

#指针类型结构体
type person struct {
	name string
	age  int8
}

func main(){
	var p = new(person)
	p.name = "snailsir"
	p.age = 18
}

# &实例化结构体
# 使用&对结构体进行取地址操作相当于对该结构体类型进行了一次new实例化操作
func main(){
	p3 := &person{}
	p3.name = "snailsir"
	p3.age = 18
}


# 没有初始化的结构体,其成员变量都是对应其类型的零值
# 键值对初始化
p4 := person{
	name: "小王子",
	age:  18,
}
# 指针初始化
p5 := &person{
	name: "小王子",
	age:  18,
}

# 嵌套结构体
# Address 地址结构体
type Address struct {
	Province string
	City     string
}

# User 用户结构体
type User struct {
	Name    string
	Gender  string
	Address
}
func main(){
	var user2 User
	user2.Name = "小王子"
	user2.Gender = "男"
	user2.Address.Province = "山东"
	user2.City = "威海" 
}

#空结构体
var v strct{}
fmt.Println(unsafe.Sizeof(v)) // 0

# json序列化
json.Marshal(c)
# json反序列化
str := `{"Title":"101","Students":[{"ID":0,"Gender":"男","Name":"stu00"}]}`
c1 := &Class{}

接口

  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
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# interface类型默认是一个指针(引用类型),如果没有对接口进行初始化就使用,就会返回nil

# 定义接口
type Sayer interface{
	say()
	move()
}
# 结构体实现Sayer接口,只要实现了接口中的所有方法,就实现了这个接口
type dog struct {}
# 值接收者
func (d dog) say{...}
# 指针接收者
func (d *dog) move{...}

# 自定义数据类型实现接口
// Sayer 接口
type Sayer interface {
	say()
}
type interger int
func (i interger) say(){
    fmt.Println("int say")
}

func main(){
    var num interger = 10
    var b Sayer = num
    b.say()
}

# 一个类型实现多个接口
// Sayer 接口
type Sayer interface {
	say()
}

// Mover 接口
type Mover interface {
	move()
}

type dog struct {
	name string
}

// 实现Sayer接口
func (d dog) say() {
	fmt.Printf("%s会叫汪汪汪\n", d.name)
}

// 实现Mover接口
func (d dog) move() {
	fmt.Printf("%s会动\n", d.name)
}


# 多个类型实现同一个接口
// Mover 接口
type Mover interface {
	move()
}
type dog struct {
	name string
}

type car struct {
	brand string
}

// dog类型实现Mover接口
func (d dog) move() {
	fmt.Printf("%s会跑\n", d.name)
}

// car类型实现Mover接口
func (c car) move() {
	fmt.Printf("%s速度70迈\n", c.brand)
}

# 接口嵌套,接口与接口间可以通过嵌套创造出新的接口
// Sayer 接口
type Sayer interface {
	say()
}

// Mover 接口
type Mover interface {
	move()
}

// 接口嵌套
type animal interface {
	Sayer
	Mover
}
type cat struct {
	name string
}

func (c cat) say() {
	fmt.Println("喵喵喵")
}

func (c cat) move() {
	fmt.Println("猫会动")
}

# 空接口
# 空接口是指没有定义任何方法的接口。因此任何类型都实现了空接口
# 空接口类型的变量可以存储任意类型的变量
func main() {
	// 定义一个空接口x
	var x interface{}
	s := "Hello 沙河"
	x = s
	fmt.Printf("type:%T value:%v\n", x, x)
	i := 100
	x = i
	fmt.Printf("type:%T value:%v\n", x, x)
	b := true
	x = b
	fmt.Printf("type:%T value:%v\n", x, x)
}

# 空接口做函数参数
# 使用空接口实现可以接收任意类型的函数参数。
func show(a interface{}) {
	fmt.Printf("type:%T value:%v\n", a, a)
}

# 空接口作为map的值
# 使用空接口实现可以保存任意值的字典。
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "沙河娜扎"
studentInfo["age"] = 18
studentInfo["married"] = false
fmt.Println(studentInfo)

# 接口和继承
package main

import "fmt"

// 猴子结构体
type Monkey struct {
	Name string
}

// 爬树方法
func (m *Monkey) climbing() {
	fmt.Println(m.Name, "生来会爬树")
}

// 继承
type LittleMonkey struct {
	Monkey
}

// 鱼接口
type Fish interface {
	swimming()
}

func (m *LittleMonkey) swimming() {
	fmt.Println(m.Name, "通过后天努力,学会了游泳")
}

func main() {
	var lmonkey LittleMonkey
	lmonkey.Name = "悟空"
	lmonkey.climbing()
	lmonkey.swimming()
}

反射

并发

  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
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# sync.WaitGroup实现goroutine同步
var wg sync.WaitGroup

func hello(num int) {
	defer wg.Done()
	fmt.Println("hello Goroutine!", num)
}

func main() {
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go hello(i)
	}
	wg.Wait()
}

# 设置2个线程来执行代码
runtime.GOMAXPROCS(2)

# 声明一个整型的通道
var ch1 chan int
var ch2 chan []int

# 初始化通道
ch3 := make(chan int)
# 把10发送到ch中
ch3 <- 10
# 从ch3中接收值
x := <- ch3
# 关闭ch
close(ch3)

# 无缓冲通道
func recv(c chan int) {
	ret := <-c
	fmt.Println("接收成功", ret)
}

func main() {
	ch := make(chan int)
	go recv(ch)
	ch <- 10
	fmt.Println("发送成功")
}

# 有缓冲通道
func main() {
	ch := make(chan int, 1) // 创建一个容量为1的有缓冲区通道
	ch <- 10
	fmt.Println("发送成功")
}

# 通道内元素数量
num := len(ch3)
# 通道内的容量
cnum := cap(ch3)

# 循环读取chan值方式1
for {
	i,ok := <-ch1
	if !ok{
		break
	}
	fmt.Println(i)
}
# 循环读取chan值方式2
for i:= range ch1{
	fmt.Println(i)
}

# 单向通道
chan<- int  # 只写通道
<-chan int  # 只读通道

# 开启3个roroutine,防止goroutine泄露和暴涨
for w:=1;w <= 3;w++{
	go worker(w)
}

# select使用
ch := make(chan int, 1)
for i := 0; i < 10; i++ {
    select {
    case x := <-ch:
        fmt.Println("out chan")
        fmt.Println(x)
    case ch <- i:
    	fmt.Println("in chan")
    }
}

#互斥锁
var x int64

var wg sync.WaitGroup
var lock sync.Mutex

func add() {
	for i := 0; i < 5000; i++ {
		lock.Lock()
		x = x + 1
		lock.Unlock()
	}
	wg.Done()
}

func main() {
	wg.Add(2)
	go add()
	go add()

	wg.Wait()
	fmt.Println(x)
}

# 读写互斥锁
var (
	x      int64
	wg     sync.WaitGroup
	lock   sync.Mutex
	rwlock sync.RWMutex
)

func write() {
	rwlock.Lock() // 加写锁
	x = x + 1
	time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒
	rwlock.Unlock()                   // 解写锁
	wg.Done()
}

func read() {
	rwlock.RLock()               // 加读锁
	time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒
	rwlock.RUnlock()             // 解读锁
	wg.Done()
}

func main() {
	start := time.Now()
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go write()
	}

	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go read()
	}

	wg.Wait()
	end := time.Now()
	fmt.Println(end.Sub(start))
}

# sync.WaitGroup
var wg sync.WaitGroup
wg.Add(delta int) # 计数器+delta
wg.Done() # 计数器-1
wg.Wait() # 阻塞直到计数器变为0

# sync.Once
var loadIconsOnce sync.Once
var myconfig map[string]string
func loadConfig(){
	
}
#加载一次mysql配置  func (o *Once) Do(f func()) {}
loadIconsOnce.Do(func(){
	myconfig = map[string]string{
		"host": "127.0.0.1",
	}
	return myconfig
})


# sync.Map
var m = sync.Map{}

func main() {
	wg := sync.WaitGroup{}
	for i := 0; i < 20; i++ {
		wg.Add(1)
		go func(n int) {
			key := strconv.Itoa(n)
			m.Store(key, n)
			val, _ := m.Load(key)
			fmt.Printf("k=:%v,v:=%v\n", key, val)
			wg.Done()
		}(i)
	}
	wg.Wait()
}


# 原子操作
package main

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

type Counter interface {
	Inc()
	Load() int64
}

// 普通版
type CommonCounter struct {
	counter int64
}

func (c CommonCounter) Inc() {
	c.counter++
}

func (c CommonCounter) Load() int64 {
	return c.counter
}

// 互斥锁版
type MutexCounter struct {
	counter int64
	lock    sync.Mutex
}

func (m *MutexCounter) Inc() {
	m.lock.Lock()
	defer m.lock.Unlock()
	m.counter++
}

func (m *MutexCounter) Load() int64 {
	m.lock.Lock()
	defer m.lock.Unlock()
	return m.counter
}

// 原子操作版
# 读取 LoadInt32
# 写入 StoreInt32
# 修改 AddInt32
# 交换 SwapInt32
# 比较交换 CompareAndSwapInt32
type AtomicCounter struct {
	counter int64
}

func (a *AtomicCounter) Inc() {
	atomic.AddInt64(&a.counter, 1) # 修改
}


func (a *AtomicCounter) Load() int64 {
	return atomic.LoadInt64(&a.counter) # 读取
}

func test(c Counter) {
	var wg sync.WaitGroup
	start := time.Now()
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			c.Inc()
			wg.Done()
		}()
	}
	wg.Wait()
	end := time.Now()
	fmt.Println(c.Load(), end.Sub(start))
}

func main() {
	c1 := CommonCounter{} // 非并发安全
	test(c1)
	c2 := MutexCounter{} // 使用互斥锁实现并发安全
	test(&c2)
	c3 := AtomicCounter{} // 并发安全且比互斥锁效率更高
	test(&c3)
}

网络编程

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 服务器

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	for {
		msg, err := proto.Decode(reader)
		if err == io.EOF {
			return
		}
		if err != nil {
			fmt.Println("decode msg failed, err:", err)
			return
		}
		fmt.Println("收到client发来的数据:", msg)
	}
}

func main() {

	listen, err := net.Listen("tcp", "127.0.0.1:30000")
	if err != nil {
		fmt.Println("listen failed, err:", err)
		return
	}
	defer listen.Close()
	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("accept failed, err:", err)
			continue
		}
		go process(conn)
	}
}

#客户端
func main() {
	conn, err := net.Dial("tcp", "127.0.0.1:30000")
	if err != nil {
		fmt.Println("dial failed, err", err)
		return
	}
	defer conn.Close()
	for i := 0; i < 20; i++ {
		msg := `Hello, Hello. How are you?`
		data, err := proto.Encode(msg)
		if err != nil {
			fmt.Println("encode msg failed, err:", err)
			return
		}
		conn.Write(data)
	}
}

udp

 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
45
46
47
48
49
50
51
52
# 服务端
func main() {
	listen, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4(0, 0, 0, 0),
		Port: 30000,
	})
	if err != nil {
		fmt.Println("listen failed, err:", err)
		return
	}
	defer listen.Close()
	for {
		var data [1024]byte
		n, addr, err := listen.ReadFromUDP(data[:]) // 接收数据
		if err != nil {
			fmt.Println("read udp failed, err:", err)
			continue
		}
		fmt.Printf("data:%v addr:%v count:%v\n", string(data[:n]), addr, n)
		_, err = listen.WriteToUDP(data[:n], addr) // 发送数据
		if err != nil {
			fmt.Println("write to udp failed, err:", err)
			continue
		}
	}
}

# 客户端
func main() {
	socket, err := net.DialUDP("udp", nil, &net.UDPAddr{
		IP:   net.IPv4(0, 0, 0, 0),
		Port: 30000,
	})
	if err != nil {
		fmt.Println("连接服务端失败,err:", err)
		return
	}
	defer socket.Close()
	sendData := []byte("Hello server")
	_, err = socket.Write(sendData) // 发送数据
	if err != nil {
		fmt.Println("发送数据失败,err:", err)
		return
	}
	data := make([]byte, 4096)
	n, remoteAddr, err := socket.ReadFromUDP(data) // 接收数据
	if err != nil {
		fmt.Println("接收数据失败,err:", err)
		return
	}
	fmt.Printf("recv:%v addr:%v count:%v\n", string(data[:n]), remoteAddr, n)
}

标准库

fmt

占位符

 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
# 通用占位符
o := struct{ name string }{"snailsir"}
fmt.Printf("%v\n", o) # {snailsir}
fmt.Printf("%+v\n", o) # {name:snailsir}
fmt.Printf("%#v\n", o) # struct { name string }{name:"snailsir"}
fmt.Printf("%T\n", o) # struct { name string }
fmt.Printf("100%%\n") # 100%

# 布尔占位符
%t

# 整型
%b # 二进制
%c # 该值对应的unicode码值
%d # 表示为十进制
%o	# 表示为八进制
%x	# 表示为十六进制,使用a-f
%X	# 表示为十六进制,使用A-F
%U	# 表示为Unicode格式:U+1234,等价于”U+%04X”
%q	# 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示

# 浮点数
%f	有小数部分但无指数部分,如123.456
%F	等价于%f

# 字符串
%s 直接输出字符串或者[]byte
%q	该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示

# 指针
%p 表示为十六进制,并加上前导的0x
%#p 表示为十六进制,没有前导的0x

# 宽度标识符
%f	默认宽度,默认精度
%9f	宽度9,默认精度
%.2f	默认宽度,精度2
%9.2f	宽度9,精度2
%9.f	宽度9,精度0

获取输入

1
2
3
4
5
6
# 方式1
fmt.Scan(&name, &age, &married)
# 方式2
fmt.Scanf("1:%s 2:%d 3:%t", &name, &age, &married)
# 方式3
fmt.Scanln(&name, &age, &married)

time

 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
later := now.Add(time.Hour)now := time.Now() #获取当前时间

year := now.Year()     #年
month := now.Month()   #月
day := now.Day()       #日
hour := now.Hour()     #小时
minute := now.Minute() #分钟
second := now.Second() #秒
fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second)

# 当前时间戳(s)
timestamp1 := now.Unix()
# 当前时间戳(纳秒)
timestamp2 := now.UnixNano()
# 获取指定时间时间戳
timeObj, _ := time.ParseInLocation("2006/01/02 15:04:05", "2019/08/04 14:15:20", time.Local)
timestamp3 := timeObj.Unix()

# 将时间戳转换成时间格式
timeObj := time.Unix(timestamp1, 0)
year := timeObj.Year()

# 二小时后时间格式
later := now.Add(time.Hour*2)

# 定时器
time.Trick(time.Second)
#每1s打印一次数据
ticker := time.Tick(time.Second)
for i:= range ticker{
	fmt.Println(i)
}

# 时间格式化 2006-1-2 15:04
# 24小时制
now.Format("2006-01-02 15:04:05.000 Mon Jan")


# 12小时制
now.Format("2006-01-02 15:04:05.000 PM Mon Jan")

# 将某个时间戳转换成时间格式
time.Unix(timestamp3, 0)

flag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# os.Args获取参数
maint.exe a b c

# 指定参数类型方式1(获得的是指针类型) flag名,默认值,帮助信息
name := flag.String("name","snailsir","姓名")

# 指定参数类型方式2
var name string
flag.StringVar(&name,"name","snailsir","姓名")

# 参数解析
flag.Parse()
#启动方式
go run main.go -name "liangdong"
go run main.go --name "liangdong"
go run main.go -name="liangdong"
go run main.go --name="liangdong"

# 获取所有参数 返回[]string类型
flag.Args()
# 获取参数个数 main.go a b c
flag.NArg()
# 获取参数个数 main.go --name="liangdong"
flag.NFlag()

log

基础

 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
# 设置logger输出选项
const (
    # 控制输出日志信息的细节,不能控制输出的顺序和格式。
    # 输出的日志在每一项后会有一个冒号分隔:例如2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
    Ldate         = 1 << iota     # 日期:2009/01/23
    Ltime                         # 时间:01:23:23
    Lmicroseconds                 # 微秒级别的时间:01:23:23.123123(用于增强Ltime位)
    Llongfile                     # 文件全路径名+行号: /a/b/c/d.go:23
    Lshortfile                    # 文件名+行号:d.go:23(会覆盖掉Llongfile)
    LUTC                          # 使用UTC时间
    LstdFlags     = Ldate | Ltime # 标准logger的初始值
)
log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)

# 设置日志前缀
log.SetPrefix("[项目名称]")

# 设置日志输出目录
log.SetOutput(logFile)



# 完整示例:
func init() {
	logFile, err := os.OpenFile("./xx.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		fmt.Println("open log file failed, err:", err)
		return
	}
	log.SetPrefix("[项目名称]")
	log.SetOutput(logFile)
	log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
}

# 上面示例等同于:
logger := log.New(os.Stdout, "[项目名称]", log.Llongfile|log.Ldate|log.Ltime)
logger.SetOutput(logFile)

文件操作

读操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 打开文件
file,err := os.Open("./file.log")

# 关闭文件
file.Close()

# Read读取文件
var tmp = make([]byte)
n,err := file.Read(tmp)
if err == io.EOF{
	fmt.Println("文件读完了")
	return
}
fmt.Printf("读取了%d字节数据\n", n)
fmt.Printf("读取内容为:", string(tmp))

# bufio读取文件
reader := bufio.NewReader(file)
line_cont,err:=reader.ReadString('\n')
fmt.Printf("第一行数据为:",line_cont)

# ioutil读取整个文件
content,err := ioutil.ReadFile("./file.log")
fmt.Println(string(content))

写操作

 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
# 文件打开模式
os.O_WRONLY	只写
os.O_CREATE	创建文件
os.O_RDONLY	只读
os.O_RDWR	读写
os.O_TRUNC	清空
os.O_APPEND	追加

# OpenFile打开文件
file,err := os.OpenFile("file.log",os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)

# write写入文件
file.Write([]byte("hello world"))
file.WriteString("hello world")

# bufio.NewWriter
writer := bufio.NewWriter(file)
#将文件写入到缓冲
writer.WriteString("hello world")
# 将缓冲中的内容写入文件
writer.Flush()


# ioutil.WriteFile
err := ioutil.WriteFile("./xx.txt", []byte("hello world"), 0666)

strconv

1
2
3
4
5
6
7
8
9
# 字符串转换成int类型
s1 := "100"
i1,err := strconv.Atoi(s1)

# 将int转换成string类型
i2 := 200
s2 := strconv.Itoa(i2)

# 

parse函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# ParseBool 字符串转换成bool
b, _ := strconv.ParseBool("1")
fmt.Printf("type:%T value:%#v\n", b, b) //type:bool value:true

# ParseInt 字符串转换成整型 10:10进制 64:int64,可为0,8,16,32,64
i, _ := strconv.ParseInt("-2", 10, 64)

# parseUnit 同ParseInt,无符号
i, err := strconv.ParseUint("2", 10, 64)

# parseFloat 字符串转换成浮点数 64:float64 
f, _ := strconv.ParseFloat("3.1415", 64)

format

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 将bool转换成string
s1 := strconv.FormatBool(true)

# 将整型转换成字符串 2:2进制
s2 := strconv.FormatInt(-2, 2) # -10
# 同FormatInt 无符号
s2 := strconv.FormatUint(2, 16)

# 将浮点数转换成字符串
# E:fmt格式 e,E十进制指数 b:二进制 g,G同e,E一样 
# -1:数字个数,-1用最少数量的、但又必需的数字来表示
# 64,float64
s3 := strconv.FormatFloat(3.1415, 'E', -1, 64)

net/http

客户端请求

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# get请求
resp, err := http.Get("https://www.baidu.com/")
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Print(string(body))

# 带参数get请求
apiUrl := "http://127.0.0.1:9090/get"
data := url.Values{}
data.Set("name", "小王子")
data.Set("age", "18")
u, err := url.ParseRequestURI(apiUrl)
u.RawQuery = data.Encode()
fmt.Println(u.String())
resp, err := http.Get(u.String())

#post请求
apiUrl := "http://127.0.0.1:9090/post"
contentType := "application/json"
data := `{"name":"snailsir","age":18}`
resp,err := http.Post(apiUrl,contentType,strings,NewReader(data))
defer resp.Body.Close()

服务端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 默认服务端
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
log.Fatal(http.ListenAndServe(":8080", nil))

# 自定义服务端
s := &http.Server{
	Addr:           ":8080",
	Handler:        myHandler,
	ReadTimeout:    10 * time.Second,
	WriteTimeout:   10 * time.Second,
	MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())

Context

https://www.liwenzhou.com/posts/Go/go_context/

框架

gin

1
2
3
4
5
6
7
8
r.GET("/hello", func(c *gin.Context) {
    c.JSON(200, gin.H{
    	"message": "Hello world!",
    })
})

# 获取gin指针
*gin.Engine

渲染

html渲染

1
2
3
4
5
6
7
8
// 加载模板文件
r.LoadHTMLGlob("templates/**/*")
// r.LoadHTMLFiles("templates/posts/index.html", "templates/users/index.html")

// 渲染
c.HTML(http.StatusOK, "users/index.html", gin.H{
    "title": "users/index",
})

静态文件

1
r.Static("/static", "./static")

json渲染

gin.H 是map[string]interface{}的缩写

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// json
c.JSON(http.StatusOK, gin.H{"message": "Hello world!"})

// AsciiJSON
c.AsciiJSON(http.StatusOK, data)

// jsonP
c.JSONP(http.StatusOK, data)

// PureJSON
c.PureJSON(200, gin.H{"html": "<b>Hello, world!</b>",})

xml渲染

1
c.XML(http.StatusOK, gin.H{"message": "Hello world!"})

ymal渲染

1
c.YAML(http.StatusOK, gin.H{"message": "ok", "status": http.StatusOK})

protobuf渲染

1
c.ProtoBuf(http.StatusOK, data)

文件上传

单文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 修改允许上传文件大小,默认32mib
router.MaxMultipartMemory = 8 << 20  // 8 MiB

// 获取单个上传文件对象
file, err := c.FormFile("file")
if err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{
        "message": err.Error(),
    })
    return
}


// 指定上传目录
dst := fmt.Sprintf("./tmp/%s", file.Filename)
// 上传文件到指定的目录
c.SaveUploadedFile(file, dst)

多文件

1
2
3
// 获取多个上传文件对象
form, _ := c.MultipartForm()
files := form.File["file"]

获取参数

querystring参数

/user/search?age=18

1
2
username := c.DefaultQuery("username","snailsir")
age:=c.Query("username")

获取form表单参数

1
2
address := c.PostForm("address")
username := c.DefaultPostForm("username", "snailsir")

获取json参数

1
2
3
4
5
b, _ := c.GetRawData()  // 从c.Request.Body读取请求数据
// 定义map或结构体
var m map[string]interface{}
// 反序列化
_ = json.Unmarshal(b, &m)

获取path参数

/user/search/snailsir/18

1
2
3
r.GET("/user/search/:username/:age", func(c *gin.Context) {
    
}

参数绑定

1
2
3
4
var login Login
if err := c.ShouldBind(&login); err == nil {
    // 绑定成功
}

重定向

http重定向

1
c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")

路由重定向

1
2
3
// 指定重定向的URL
c.Request.URL.Path = "/test2"
r.HandleContext(c)

路由

普通路由

1
2
3
4
5
6
7
8
r.GET("/index",func(c *gin.Context){...})
r.POST("/index",func(c *gin.Context){...})
r.DELETE("/index",func(c *gin.Context){...})
r.PUT("/index",func(c *gin.Context){...})
// 没有路由
r.NoRoute(func(c *gin.Context) {...})
// 所有请求方式都支持
r.Any("/index", func(c *gin.Context) {...})

路由分组

1
2
3
4
5
6
7
8
userGroup := r.Group("/user")
{
    userGroup.GET("/index", func(c *gin.Context) {...})
}

// 等同于
userGroup := r.Group("/user")
userGroup.GET("/index", func(c *gin.Context) {...})

中间件

定义中间件

1
2
3
func StatCost() gin.HandlerFunc {
    return func(c *gin.Context) {...}
}

中间件使用

1
2
3
4
5
6
7
8
9
// 注册一个全局中间件
r.Use(StatCost())
// 路由单独注册中间件
r.GET("/tes", StatCost(), func(c *gin.Context) {...})
// 路由组使用
shopGroup := r.Group("/shop", StatCost())
// 或者
shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())

使用 Hugo 构建
主题 StackJimmy 设计