​​​​ golang 学习笔记 | 苏生不惑的博客

golang 学习笔记

interface 接口

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
//https://learnku.com/articles/25488
type Pet interface {
SetName(name string)
Name() string
Category() string
}

type Dog struct {
name string
}

func (dog *Dog) SetName(name string) {
dog.name = name
}

func (dog Dog) Name() string {
return dog.name
}

func (dog Dog) Category() string {
return "dog"
}

func TestDog(t *testing.T) {
dog := Dog{"little pig"}
_, ok := interface{}(dog).(Pet)
fmt.Printf("Dog implements interface Pet: %v\n", ok) // Dog implements interface Pet: false
_, ok = interface{}(&dog).(Pet)
fmt.Printf("*Dog implements interface Pet: %v\n", ok) // *Dog implements interface Pet: true
var pet Pet = &dog
fmt.Printf("This pet is a %s, the name is %q.\n",
pet.Category(), pet.Name()) // This pet is a dog, the name is "little pig".

dog.SetName("monster")
fmt.Printf("This pet is a %s, the name is %q.\n",
pet.Category(), pet.Name()) // This pet is a dog, the name is "monster".
}

Go 陷阱之 for 循环迭代变量

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
var slice []func()
//https://learnku.com/articles/26861
func main() {
sli := []int{1, 2, 3, 4, 5}
for _, v := range sli {
fmt.Println(&v)
slice = append(slice, func(){
fmt.Println(v * v) // 直接打印结果
})
}

for _, val := range slice {
val()
}
}
// 输出 25 25 25 25 25

var slice []func()

func main() {
sli := []int{1, 2, 3, 4, 5}
for _, v := range sli {
temp := v // 其实很简单 引入一个临时局部变量就可以了,这样就可以将每次的值存储到该变量地址上
fmt.Println(&temp) // 这里内存地址是不同的
slice = append(slice, func(){
fmt.Println(temp * temp) // 直接打印结果
})
}

for _, val := range slice {
val()
}
}
// 输出 1, 4, 9, 16, 25 预期结果
for i, v := range
i, v 都是只创建一次,然后循环中赋值。
另外,循环的数组或 Map,是在开始前的镜像,循环中添加或移除元素不改变其循环次数。
关于循环还可以引申的是 Map 时是无序的

Struct 与 面向对象

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
type Human struct {
name string
age int
}
var tom Human
// 通过赋值初始化 https://learnku.com/articles/25094
tom.name, tom.age = "Tom", 18
// 详细初始化
jerry := Human{age:25, name:"Jerry"}
// 按照结构体声明顺序
peter := Human{"Peter", 34}
// 通过 . 访问
fmt.Printf("%s is %d old\n", tom.name, tom.age)

type Staff struct {
Human // 隐式的引入 Human 的字段
wage int
age float32 // 覆盖 Human 的 age
}
tom := Staff{Human{"Tom", 18}, 1000, 18.5}
jerr := Staff{Human:Human{age:32, name:"Jerr"}, wage: 2000, age: 32.5}
fmt.Printf("%s is %f old,%d years, wage is %d", tom.name, tom.Human.age, tom.age, tom.wage)
fmt.Printf("%s is %f old,%d years, wage is %d", jerr.name, jerr.Human.age, jerr.age, jerr.wage)
type Human struct {
name string
age int
}

type Employee struct {
Human
wage int
}

func (h *Human) Say() {
fmt.Printf("Hi, I am %s, My age is %d", h.name, h.age)
}

// Employee 重写 Say
func (e *Employee) Say() {
fmt.Printf("Hi, I am %s, %d old year, wage is %d per mothn", e.name, e.age, e.wage)
}

异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
func TestPanic(t *testing.T) {
defer func() {
fmt.Println("最后结果依旧执行!") // 这一部分的代码依旧执行
}()
fmt.Println("执行开始")
panic(errors.New("错误信息!"))
}
// 输出大致结果如下
开始
最后结果依旧执行!
--- FAIL: TestPanic (0.00s)
panic: 错误信息! [recovered]
panic: 错误信息!

rand.Intn () 生成的是伪随机数

1
2
3
4
5
6
7
8
9
10
11
12
13
rand.Intn() 函数是个伪随机函数, 不管运行多少次都只会返回同样的随机数, 因为它默认的资源就是单一值, 所以必须调用 rand.Seed(), 并且传入一个变化的值作为参数, 如 time.Now().UnixNano() , 就是可以生成时刻变化的值.

package main

import ("fmt"
"math/rand"
"time")

func main() {
// 初始化随机数的资源库, 如果不执行这行, 不管运行多少次都返回同样的值https://learnku.com/articles/26011
rand.Seed(time.Now().UnixNano())
fmt.Println("A number from 1-100", rand.Intn(81))
}

beego 框架 model curd

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
定义要连接的数据库 main.go https://learnku.com/articles/26029#topnav

package main
import (
_ "newgo/routers"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
)
func init() {
orm.RegisterDriver("mysql", orm.DRMySQL)
orm.RegisterDataBase("default", "mysql", "root:root@tcp(127.0.0.1:3306)/go?charset=utf8")
}
func main() {
beego.Run()
}
定义路由 router.go

beego.Router("/type", &controllers.ClassifyController{}, "GET:Type")
beego.Router("/classifyinsert", &controllers.ClassifyController{}, "POST:ClassifyInsert")
beego.Router("/classifyupdate", &controllers.ClassifyController{}, "GET:ClassifyUpdate")
beego.Router("/classifysave", &controllers.ClassifyController{}, "POST:ClassifySave")
beego.Router("/classifydel", &controllers.ClassifyController{}, "GET:ClassifyDel")
model书写

package models
import (
"github.com/astaxie/beego/orm"
)
type Classify struct {
Id int
Name string
Content string
}
func init() {
orm.RegisterModel(new(Classify))
}
//增加操作
func InsertClassify(name string, content string) (int64, error) {
o := orm.NewOrm()
var clasinfo Classify
clasinfo.Name = name
clasinfo.Content = content
id, err := o.Insert(&clasinfo)
return id, err
}
//删除操作
func ClassifyDel(id int) ([]Classify, int64, error) {
o := orm.NewOrm()
var lists []Classify
num, err := o.QueryTable("classify").Filter("Id", id).Delete()
o.QueryTable("inventory").Filter("Cid", id).Delete()
return lists, num, err
}
//展示
func ClassifyList() ([]Classify, int64, error) {
o := orm.NewOrm()
var lists []Classify
num, err := o.QueryTable("classify").All(&lists)
return lists, num, err
}
//修改
func ClassifyUpdate(id int, name string, content string) ([]Classify, int64, error) {
o := orm.NewOrm()
var lists []Classify
num, err := o.QueryTable("classify").Filter("Id", id).Update(orm.Params{
"name": name,
"content": content,
})
return lists, num, err
}
控制器

package controllers
import (
"fmt"
"newgo/models"
"strings"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego"
)
type ClassifyController struct {
beego.Controller
}
func (c *ClassifyController) Get() {
c.TplName = "index.html"
}
func (c *ClassifyController) Type() {
list, num, err := models.ClassifyList()
if err == nil {
fmt.Println(list)
fmt.Println(num)
}
c.Data["comment"] = list
fmt.Println(list)
c.TplName = "type.html"
}
//添加post
func (c *ClassifyController) ClassifyInsert() {
content := strings.TrimSpace(c.GetString("content"))
name := strings.TrimSpace(c.GetString("name"))
models.InsertClassify(name, content)
c.Ctx.Redirect(302, "/type")
}
//修改get展示
func (c *ClassifyController) ClassifySave() {
id, _ := c.GetInt("id")
fmt.Println(id)
content := strings.TrimSpace(c.GetString("content"))
name := strings.TrimSpace(c.GetString("name"))
models.ClassifyUpdate(id, name, content)
c.Ctx.Redirect(302, "/type")
}
//删除
func (c *ClassifyController) ClassifyDel() {
id, _ := c.GetInt("id")
models.ClassifyDel(id)
c.Ctx.WriteString("200")
}
//修改post方法
func (c *ClassifyController) ClassifyUpdate() {
id, _ := c.GetInt("id")
o := orm.NewOrm()
type Comment struct {
Id int
Name string
Content string
}
var comments []Comment
var com []Comment
num, err := o.Raw("select * from classify where id=?", id).QueryRows(&comments)
o.Raw("select * from classify").QueryRows(&com)
if err == nil {
fmt.Println(num)
fmt.Println(comments)
fmt.Println(com)
c.Data["updates"] = comments
c.Data["comment"] = com
} else {
fmt.Println("查询报错")
}
c.TplName = "type.html"
}

php go的类

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
<?php
namespace App;
class Animal
{
public $name;
public $height;
public $weight;
public function __construct($name, $height, $weight)
{
$this->name = $name;
$this->height = $height;
$this->weight = $weight;
}
public function getInfo()
{
return [
'name' => $this->name,
'height' => $this->height,
'weight' => $this->weight,
];
}
}
//https://learnku.com/articles/26056
package main
type Animal struct {
Name string
Height uint
Weight uint
}
func NewAnimal(name string, height, weight uint) Animal {
return Animal{
Name: name,
Height: height,
Weight: weight,
}
}
func (a *Animal) GetInfo() map[string]interface{} {
return map[string]interface{}{
"name": a.Name,
"height": a.Height,
"weight": a.Weight,
}
}

Go for PHP Developers: Structs vs Classes

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
package main
type Animal struct {
Name string
Height uint
Weight uint
}
func NewAnimal(name string, height, weight uint) Animal {
return Animal{
Name: name,
Height: height,
Weight: weight,
}
}
//https://learnku.com/articles/26056#reply85350 定义不同类似的结构体
func (a *Animal) GetInfo() map[string]interface{} {
return map[string]interface{}{
"name": a.Name,
"height": a.Height,
"weight": a.Weight,
}
}
package main
type AnimalContract interface {
GetInfo() map[string]interface{}
}
type Animal struct {
// Struct def...
}
func (a *Animal) GetInfo() map[string]interface{} {
// Method body... 继承AnimalContract 如果我们定义一个定义该接口的结构,它将隐式继承它。
}
package main
import "fmt"
func main(){
var array = [9]interface{}{1,3,4,6,7,8,9,10,"index"}
fmt.Print(array)
}

append 扩容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package array

import (
"fmt"
)

/**
* arr 底层扩容知识点https://learnku.com/articles/27630#topnav
*/
func ArrayAppend() []int {
arr := make([]int,5)
fmt.Printf("arr.len: %d; arr.cap: %d \n", len(arr),cap(arr))
arr = append(arr,10)
//问现在 arr 结果是什么
fmt.Printf("arr.len: %d; arr.cap: %d \n", len(arr),cap(arr))
return arr
}

go build

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
工作区是 Go 便捷管理项目的方式。一言以蔽之,它就是你系统上的一个目录,Go 可以在此 查找源码 文件,管理依赖包 还有 分发二进制文件。 当 Go 程序命中 import 语句,它就会去 Go 标准库 (*$GOROOT/src*) 中寻找响应的包。如果没找到,然后 Go 就会引用环境变量 GOPATH ,它是 Go 工作区目录的路径,然后再去 $GOPATH/src 目录下寻找这个包。

你可以按需指定多个工作区,只要你保证 GOPATH 环境变量指向你的工作区目录即可。
一个 Go 工作区目录必须有三个子目录也就是: src, pkg and bin
src 目录包含 Package. Package 包括一个包含 Go 源代码 (*.go* files) 。 任何使用 'go get' 命令安装的包也将驻留在这里 (及其依赖包)。

在 Go 中,每个程序都包含在一个包中。 因此,无论何时您将使用新的 Go 项目,您都需要在 $GOPATH/src 中创建新目录并向上工作。
当执行 go run hello.go 命令时,Go 编译器会首先编译 hello.go 文件,然后执行里面的二进制代码。

Go 程序支持输出二进制文件,执行 go build <package-name> (main 包) 或 go build program/path.go 命令即可在当前文件夹创建二进制文件。
执行 go install 命令就可以创建这些文件。 go install 命令会触发底层的 go build 命令,然后将这些文件保存到 bin 目录中。通常情况下,这个目录是在系统的可执行路径中。因此,这个目录中的所有文件都是可以通过终端来操作的。
https://learnku.com/golang/t/26863
go run main.go
go build main.go
go install test #main.go在test目录

for 循环迭代变量

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
var slice []func()

func main() {
sli := []int{1, 2, 3, 4, 5}
for _, v := range sli {
fmt.Println(&v)
slice = append(slice, func(){
fmt.Println(v * v) // 直接打印结果
})
}
//v 在 for 循环引进的一个块作用域内进行声明。在循环里创建的所有函数变量共享相同的变量,就是一个可访问的存储位置,而不是固定的值。(你会惊奇的发现 &v 的内存地址是一样的)https://www.njphper.com/posts/974e86a6.html
for _, val := range slice {
val()
}
}
// 输出 25 25 25 25 25
var slice []func()

func main() {
sli := []int{1, 2, 3, 4, 5}
for _, v := range sli {
temp := v // 其实很简单 引入一个临时局部变量就可以了,这样就可以将每次的值存储到该变量地址上
fmt.Println(&temp) // 这里内存地址是不同的
slice = append(slice, func(){
fmt.Println(temp * temp) // 直接打印结果
})
}

for _, val := range slice {
val()
}
}
// 输出 1, 4, 9, 16, 25 预期结果

实现 PHP 的密码加密解密

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
package main
#https://www.njphper.com/posts/f3f0ab9c.html
import (
"fmt"
"log"
"golang.org/x/crypto/bcrypt"
)

func main() {
for {
// 输入密码 获取 hash 值
pwd := getPwd()
hash := hashAndSalt(pwd)
// 再次输入密码验证
pwd2 := getPwd()
pwdMatch := comparePasswords(hash, pwd2)
fmt.Println("Passwords Match?", pwd)
}
}

func getPwd() []byte {
fmt.Println("Enter a password")
var pwd string
_, err := fmt.Scan(&pwd)
if err != nil {
log.Println(err)
}
return []byte(pwd)
}


func hashAndSalt(pwd []byte) string {
hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost)
if err != nil {
log.Println(err)
}
return string(hash)
}

func comparePasswords(hashedPwd string, plainPwd []byte) bool {
byteHash := []byte(hashedPwd)

err := bcrypt.CompareHashAndPassword(byteHash, plainPwd)
if err != nil {
log.Println(err)
return false
}
return true
}
str := `{"page": 1, "fruits": ["apple", "peach"]}`
实例化了一个 json 字符串,fruits 对应的是一个数组。
在 Go 语言中,字符串字面量可以使用双引号 "" 或者反引号 ' 来创建

截取中文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
first := "fisrt"
fmt.Println([]rune(first))
fmt.Println([]byte(first))
[]rune(s), 它可以将字符串转化成 unicode 码点
byte 表示一个字节,rune 表示四个字节
first := "社区"
fmt.Println([]rune(first))
fmt.Println([]byte(first))
[31038 21306] //输出结果[]rune
[231 164 190 229 140 186]//输出结果[]byte
s := "截取中文"
//试试这样能不能截取?
fmt.Println(s[:2])

s := "截取中文"
//试试这样能不能截取? https://www.njphper.com/posts/c251fcd3.html
res := []rune(s)
fmt.Println(string(res[:2]))

动态规划

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
硬币问题:如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元?https://blog.mutoe.com/2019/dynamic-programming/
// CoinChange: coins 硬币, amount 期望的金额, 返回最少需要的硬币数量,如果不可解返回-1
func CoinChange(coins []int, amount int) int {
dp := make([]int, amount+1)
dp[0] = 0

for i := 1; i <= amount; i++ {
dp[i] = amount + 1
for _, coin := range coins {
if coin <= i && dp[i-coin] != -1 && dp[i-coin]+1 < dp[i] {
dp[i] = dp[i-coin] + 1
}
}
if dp[i] > amount {
dp[i] = -1
}
}

return dp[amount]
}

go php 对比学习

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
https://learnku.com/golang/t/23385/from-a-php-engineers-point-of-view-go

first, second = second, first
func getName() (string, string) {
return "hello", "world"
}

first, last = getName()
// foreach ($bookings as $key => $booking) {}
for key, booking := range bookings {}

// for ($i = 0; $i < count($bookings); $i++) {}
for i := 0; i < len(bookings); i++ {}

// while ($i < count($bookings)) {}
for i < len(bookings) {}

// do {} while (true);
for {}
type rect struct { // 定义一个结构体
width int
height int
}

func (r *rect) area() int { // 在结构体上添加方法
return r.width * r.height
}

r := rect{width: 10, height: 15} // 初始化
fmt.Print(r.area())
type Employee struct {
Name string
Job Job
}

type Job struct {
Employer string
Position string
}

// 并去结构化它
e := Employee{
Name: "Sobit",
Job: {
Employer: "GetYourGuide",
Position: "Software Engineer",
},
}func heartbeat() {
for {
time.Sleep(time.Second)
fmt.Println("I'm still running...")
}
}
现在, 我们怎样才能让这个函数在后台执行并且允许我们并行的做其他事呢? 答案比你想象中要简单, 只需要在执行函数前加一个 go :

go heartbeat()

// 继续做其他事
Go 自带开箱即用关注代码风格的 go fmt 工具。 不再需要分享 IDE 的配置文件, 不用尝试记住大括号应该是在同一行还是再起一行。

可以使用 go doc 去阅读源码包文档, 而 go vet 将会协助我们查找代码中的问题。 安装第三方包只需要执行 go get [github.com/[vendor]/[package](http://github.com/[vendor]/[package)] 指令, 测试只需要执行 go test [package] 指令。

用Golang写爬虫

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
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
fmt.Println("Hello, world")
url := "http://www.baidu.com/"
download(url)
}
func download(url string) {
client := &http.Client{}
req, _ := http.NewRequest("GET", url, nil)
// 自定义Header
req.Header.Set("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)")
resp, err := client.Do(req)
if err != nil {
fmt.Println("http get error", err)
return
}
//函数结束后关闭相关链接https://zhangslob.github.io/2019/01/16/Golang%E5%86%99%E7%88%AC%E8%99%AB/
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("read error", err)
return
}
fmt.Println(string(body))
}

package main
import (
"fmt"
"github.com/jackdanger/collectlinks"
"net/http"
)
func main() {
fmt.Println("Hello, world")
url := "http://www.baidu.com/"
download(url)
}
func download(url string) {
client := &http.Client{}
req, _ := http.NewRequest("GET", url, nil)
// 自定义Header
req.Header.Set("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)")
resp, err := client.Do(req)
if err != nil {
fmt.Println("http get error", err)
return
}
//函数结束后关闭相关链接
defer resp.Body.Close()
links := collectlinks.All(resp.Body)
for _, link := range links {
fmt.Println("parse url", link)
}
}

go 实现一个简单的 mvc

1
2
3
4
5
6
git clone https://github.com/jc91715/go-simple-mvc
go get github.com/astaxie/beego
go get github.com/go-sql-driver/mysql

go run main.go
https://learnku.com/articles/29546

时间戳操作大全

1
2
3
4
5
6
7
8
9
10
11
12
dateTime := time.Now()
fmt.Println(dateTime)
year := time.Now().Year() //年

fmt.Println(year)

month := time.Now().Month() //月
fmt.Println(month)

fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
t := time.Date(2014, 1, 7, 5, 50, 4, 0, time.Local).Unix()
https://learnku.com/golang/t/30925

request

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
package http

import (
"fmt"

"github.com/imroc/req"
logger "xxx" //logger日志封装
util "xxxx/sdk-utils"
)

type method string

const (
GET method = "GET"
POST method = "POST"
PUT method = "PUT"
PATCH method = "PATCH"
DELETE method = "DELETE"
)

type Response struct {
Data []byte
}

//反序列化操作
func (r *Response) Deserialize(object interface{}) (err error) {
err = util.JSONUnmarshal(r.Data, object)
return err
}

//支持所有类型请求.
func Request(method method, url string, v ...interface{}) (response *Response, err error) {
var r *req.Resp
response = new(Response)
switch method {
case "GET":
r, err = req.Get(url, v...)
case "POST":
r, err = req.Post(url, v...)
case "PUT":
r, err = req.Put(url, v...)
case "PATCH":
r, err = req.Patch(url, v...)
case "DELETE":
r, err = req.Delete(url, v...)
}

if err != nil {
return
}

statusCode := r.Response().StatusCode
if statusCode != 200 {
err = fmt.Errorf("http cilent code err: %d", statusCode)
return
}

response.Data = r.Bytes()
return
}
https://learnku.com/articles/30935

golang代理

1
2
export GO111MODULE=on
export GOPROXY=https://goproxy.io

递归打印杨辉三角

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

import "fmt"

func main() {
YangHuiTriangle(10)
}
//https://learnku.com/articles/31290
func YangHuiTriangle(n int) []int {
i := n - 1
line := make([]int, n)
if i <= 0 {
line = []int{1}
fmt.Println(line)
return line
}
down := YangHuiTriangle(i)
for x := 0; x < n; x++ {
if x == 0 {
line[0] = 1
continue
}
if x == n-1 {
line[x] = 1
continue
}
line[x] = down[x-1] + down[x]
}
fmt.Println(line)
return line
}

JSON 解析

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

import (
"fmt"
"encoding/json"
"time"

)

func main() {
type Fruit struct {
Name string `json:"Name"`
PriceTag string `json:"PriceTag"`
}

type FruitBasket struct {
Name string
Fruit map[string]Fruit
Id int64 `json:"ref"`// 声明对应的json key
Created time.Time

}
jsonData := []byte(`
{
"Name": "Standard",
"Fruit" : {
"1": {
"Name": "Apple",
"PriceTag": "$1"
},
"2": {
"Name": "Pear",
"PriceTag": "$1.5"
}
},
"ref": 999,
"Created": "2018-04-09T23:00:00Z"
}`)

var basket FruitBasket
err := json.Unmarshal(jsonData, &basket)
if err != nil {
fmt.Println(err)
}
for _, item := range basket.Fruit {
fmt.Println(item.Name, item.PriceTag)
}
}
//https://learnku.com/articles/31320

go 的切片

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
对于 go 语言的数组,copy 和 view 是同时都存在的。

copy 就是使用这个数组的时候我将这个数组拷贝一份,这样对于数组的增删改,是不会改变原数组的值的
view 由数组执行切片所返回的对象是一个 view,即视图,若我们在视图上操作数组,会改变原数组,
copy 场景

package main

import (
"fmt"
)

func updateArr(arr [5]int) {
arr[0] = 100
fmt.Println("修改后的arr:", arr)
}

func main() {
arr3 := [...]int{2, 4, 5, 6, 7}
fmt.Println("原来的:", arr3)
updateArr(arr3)
fmt.Println("再次查看原始的:", arr3)
}
输出结果:
原来的: [2 4 5 6 7]
修改后的arr: [100 4 5 6 7]
再次查看原始的: [2 4 5 6 7]
如上代码可以看到,我们在 updateArr 里面修改了下标为 0 的值,但是我们输出原始数组的时候,并没有变。这就是对数组 copy。

view 场景

func updateArr(arr []int) {
arr[0] = 100
fmt.Println("修改后的arr:", arr)
}

func main() {
arr3 := [...]int{2, 4, 5, 6, 7}
fmt.Println("原来的:", arr3)
// 使用切片
updateArr(arr3[:])
fmt.Println("再次查看原始的:", arr3)
}
输出结果:
原来的: [2 4 5 6 7]
修改后的arr: [100 4 5 6 7]
再次查看原始的: [100 4 5 6 7]
为什么 view 能够改变原数组

虽然 Slice 本身是值类型,但是它内部使用了对数组的指针引用,所以修改切片数据,会将数组原有数据修改掉。

当然,在理解上面的同时,一定要知道 go 是如何定义一个切片的

var b []int
所以,在 updateArr 这个函数传参的时候 arr []int 是传切片进去。不然会报错 https://learnku.com/articles/32171

结构体的值传递和引用传递

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
值传递

type person struct {
name string
age int
gender string
}

// 方法定义
func (p person) setName(name string) {
p.name = name
fmt.Println("person name is:", p.name)
}

func (p *person) describe() {
fmt.Printf("我叫 %v, 我今年 %v 了", p.name, p.age)
}

func main() {
p := person{"Tom", 22, "man"}
// 修改name的值
p.setName("jury")
p.describe()
}
如上,我们在 setName 这个函数的接受者 是 p person。 此时,就相当于把 person{"Tom", 22, "man"} 拷贝一份传递过去。
引用传递

引用传递就是将该值在内存中的存储地址传递过去。

type person struct {
name string
age int
gender string
}

// 方法定义
func (p *person) setName(name string) {
p.name = name
fmt.Println("person name is:", p.name)
}

func (p *person) describe() {
fmt.Printf("我叫 %v, 我今年 %v 了", p.name, p.age)
}

func main() {
p := person{"Tom", 22, "man"}
// 修改name的值
p.setName("jury")
p.describe()
}
我们在 setName 这个函数的接受者 是 p *person。 此时,就相当于把 person{"Tom", 22, "man"} 存储地址传递过去。
https://learnku.com/articles/32233

实现继承

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

import (
"fmt"
"strconv"
)

// 动物类
type Animal struct {
name string
subject string
}

// 动物的公共方法
func (a *Animal) eat(food string) {
fmt.Println(a.name + "喜欢吃:" + food +",它属于:" + a.subject)
}

// 猫类,继承动物类https://learnku.com/articles/32295#reply102908
type Cat struct {
// 继承动物的属性和方法
Animal
// 猫自己的属性
age int
}

// 猫类独有的方法 type name struct{} 结构体 就相当于其他语言中的 class 类的概念。
func (c Cat) sleep() {
fmt.Println(c.name + " 今年" + strconv.Itoa(c.age) + "岁了,特别喜欢睡觉")# go 语言中,string + int,如果想要一个字符串,则需要对 int 类型的值转换为 string 类型,然后才能拼接。
}

func main() {
// 创建一个动物类
animal := Animal{name:"动物", subject:"动物科"}
animal.eat("肉")

// 创建一个猫类
cat := Cat{Animal: Animal{name:"咪咪", subject:"猫科"},age:1}
cat.eat("鱼")
cat.sleep()
}
输出结果:
动物喜欢吃:肉,它属于:动物科
咪咪喜欢吃:鱼,它属于:猫科
咪咪 今年1岁了,特别喜欢睡觉

###

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
如果之前学过 PHP 或者其他语言,可以知道实现一个接口,不仅需要实现其接口里的方法,也需要使用 implements 显式说明其实现了该接口。
go 则是 duck type,即像鸭子,那它就可以是个鸭子。也就是说,在 go 中,是要是结构体实现了某个接口指定的方法,那它就是实现了这个接口,不需要使用 implements 显式说明。

现在说一下接口优点,最大的莫过于控制反转和解耦。举个例子,比如设计一个发送短信的服务,我想接入阿里云与腾讯云的短信业务,此时应该如何设计比较好?是在最底层通过 type 分类来区分,还是在上层通过传入不同的服务,来对应发送不同的运营商短信比较优雅?其实看 laravel 框架上的实现就很明白了。

同时,go 中的多态的特性通过接口来展现的。

package main

import "fmt"

type Usb interface {
Start()
Stop()
}

type Phone struct {
Name string
}

func (p Phone) Start() {
p.Name = "张三" //这里对引用传递还是值传递的作用一样生效,所以这里没有变化
fmt.Println("手机开始工作了...")
}

func (p Phone) Stop() {
fmt.Println(p.Name) //李四
fmt.Println("手机停止工作了...")
}

type Carame struct {
Name string
}

func (c *Carame) Start() {
c.Name = "张三" //这里对引用传递还是值传递的作用一样生效,所以这里有变化
fmt.Println("相机开始工作了...")
}
func (c *Carame) Stop() {
fmt.Println(c.Name) //张三
fmt.Println((*c).Name) //张三
fmt.Println("相机停止工作了...")
}

type Computer struct {
}

//Usb是前面定义的接口,这里调用的2个函数,在结构体中一定都要有声明过,否则报错。
func (com *Computer) Working(usb Usb) {
usb.Start()
usb.Stop()
}

func main() {
var phone = Phone{}
phone.Name = "李四"
var carame = Carame{}
carame.Name = "李四"
var computer = Computer{}
//这里传入参数是地址还是对象,需要依靠原结构体里定义的方法来判断(比如Phone结构体)。比如Phone结构体中的start方法是p,此时Working传入对象还是地址都可以。如果是*p,则必须传入地址。与com的类型无关。
computer.Working(&phone)
computer.Working(&carame)
//最后输出
// 手机开始工作了...
// 李四
// 手机停止工作了...
// 相机开始工作了...
// 张三
// 相机停止工作了...
}
https://learnku.com/golang/t/32266

面向对象特性

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
接口使用 interface 关键字声明,任何实现接口定义方法的类都可以实例化该接口,接口和实现类之间没有任何依赖
type Sayer interface {
Say(message string)
SayHi()
}
继承使用组合的方式实现

type Animal struct {
Name string
}

func (a *Animal) Say(message string) {
fmt.Printf("Animal[%v] say: %v\n", a.Name, message)
}

type Dog struct {
Animal
}
Dog 将继承 Animal 的 Say 方法,以及其成员 Name
子类可以重新实现父类的方法

// override Animal.Say
func (d *Dog) Say(message string) {
fmt.Printf("Dog[%v] say: %v\n", d.Name, message)
}
Dog.Say 将覆盖 Animal.Say
var sayer Sayer

sayer = &Dog{Animal{Name: "Yoda"}}
sayer.Say("hello world")

使用 Proxy 突破网管的限制

1
2
3
4
5
6
7
8
9
10
11
12
https://gitee.com/snail/proxy
为了绕开公司网络的监测,需要加密本地发往代理服务器的数据,然后由代理服务器解密数据,再发往真正的服务器,因些我们需要两级代理
./proxy keygen -C proxy
执行命令会在当前目录下生成 proxy.key 和 proxy.crt 两个文件,将这两个文件复制到本机的 Proxy 安装目录中
在 VPS 上执行命令:
./proxy socks -t tls -p ":3333" -C proxy.crt -K proxy.key --daemon --forever --log proxy.log

本机打开 Proxy 安装目录中的 “bootstrap.bat” 文件,将 “proxy.exe ...” 这一行修改为:

proxy.exe socks -p ":3334" -t tcp -T tls -P "IP:3333" -C proxy.crt -K proxy.key --debug
其中小写 "p" 表示本机监听端口,大写 “P” 表示 VPS 端监听地址,小写 “t” 表示本机使用的传输协议,大写 “T” 表示 VPS 端使用的传输协议。保存文件后,双击 “bootstrap.bat” 即可启动 Proxy
https://learnku.com/articles/32369

go 执行系统命令时不能直接使用管道符

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
方法一(异步)

func async() {
cmd1 := exec.Command("ls", ".")
cmd2 := exec.Command("grep", "go")
out, _ := cmd1.StdoutPipe()
in, _ := cmd2.StdinPipe()
cmd2.Stdout = os.Stdout
go func() {
defer func() {
out.Close()
in.Close()
}()
io.Copy(in, out)
}()
cmd1.Run()
cmd2.Run()
}
方法二(同步)

func sync() {
cmd1 := exec.Command("ls", ".")
cmd2 := exec.Command("grep", "go")
in, _ := cmd2.StdinPipe()
cmd1.Stdout = in
cmd2.Stdout = os.Stdout
cmd2.Start()
cmd1.Run()
in.Close()
cmd2.Wait()
}
方法三(多个)

func main() {
cmd1 := exec.Command("ls", ".")
cmd2 := exec.Command("grep", "go")
cmd3 := exec.Command("grep", "main")
cmd3.Stdout = os.Stdout
pipe(cmd1, cmd2, cmd3)
cmd1.Run()
cmd2.Run()
cmd3.Run()
}

func pipe(cmds ...*exec.Cmd) {
for i, cmd := range cmds {
if i > 0 {
out, _ := cmds[i-1].StdoutPipe()
in, _ := cmd.StdinPipe()
go func() {
defer func() {
out.Close()
in.Close()
}()
io.Copy(in, out)
}()
}
}
}
https://learnku.com/articles/33592

实现一个 web 服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"io"
"log"
"net/http"
)

func main() {
http.HandleFunc("/hello", helloHandler)
err := http.ListenAndServe(":8000", nil)
if err != nil {
log.Fatal("ListenAndServe:", err.Error())
}
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "hello, world!")
}
$ go run hello.go
$ curl http://localhost:8080/hello
hello, world!
https://learnku.com/golang/wikis/26652

Go 爬虫框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
https://github.com/zhshch2002/goribot  
package main

import (
"encoding/json"
"fmt"
"github.com/zhshch2002/goribot"
)

func main() {
s := goribot.NewSpider()
_ = s.Get(nil, "https://httpbin.org/get?Goribot%20test=hello%20world", func(r *goribot.Response) {
m := make(map[string]interface{})
err := json.Unmarshal([]byte(r.Text), &m)
if err != nil {
fmt.Println(err)
}
fmt.Println(m)
})
s.Run()
}
https://learnku.com/golang/t/33980

###go命令行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
go get -u github.com/cosmos72/gomacro
λ gomacro.exe
// GOMACRO, an interactive Go interpreter with generics and macros
// Copyright (C) 2018-2019 Massimiliano Ghilardi <https://github.com/cosmos72/gomacro>
// License MPL v2.0+: Mozilla Public License version 2.0 or later <http://mozilla.org/MPL/2.0/>
// This is free software with ABSOLUTELY NO WARRANTY.
//
// Type :help for help
gomacro> a:=1
gomacro> a
1 // int
go get -u github.com/motemen/gore/cmd/gore
❯ gore
gore version 0.4.1 :help for help
gore> a := 1
1
gore> a + 2
3
gore> func Add(a, b int) int { return a + b }
gore> Add(1, 2)
3

go cron 秒

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

import (
"fmt"
"time"

"github.com/robfig/cron"
)

func main() {
c := cron.New()

c.AddFunc("*/1 * * * * *", func() {
fmt.Println("every 1 seconds executing")
})

go c.Start()
defer c.Stop()

select {
case <-time.After(time.Second * 10):
return
}
}https://learnku.com/articles/34456
Github:github.com/robfig/cron

Go 使用反射导出 Excel

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
type Record struct {
Name string `xlsx:"A-姓名"`
Age int32 `xlsx:"B-年齡"`
}
func RefactorWrite(records []*Record) {
xlsx := excelize.NewFile()
index := xlsx.NewSheet("Sheet1")

for i, t := range records {
d := reflect.TypeOf(t).Elem()
for j := 0; j < d.NumField(); j++ {
// 设置表头
if i == 0 {
column := strings.Split(d.Field(j).Tag.Get("xlsx"), "-")[0]
name := strings.Split(d.Field(j).Tag.Get("xlsx"), "-")[1]
xlsx.SetCellValue("Sheet1", fmt.Sprintf("%s%d", column, i+1), name)
}
// 设置内容
column := strings.Split(d.Field(j).Tag.Get("xlsx"), "-")[0]
switch d.Field(j).Type.String() {
case "string":
xlsx.SetCellValue("Sheet1", fmt.Sprintf("%s%d", column, i+2), reflect.ValueOf(t).Elem().Field(j).String())
case "int32":
xlsx.SetCellValue("Sheet1", fmt.Sprintf("%s%d", column, i+2), reflect.ValueOf(t).Elem().Field(j).Int())
case "int64":
xlsx.SetCellValue("Sheet1", fmt.Sprintf("%s%d", column, i+2), reflect.ValueOf(t).Elem().Field(j).Int())
case "bool":
xlsx.SetCellValue("Sheet1", fmt.Sprintf("%s%d", column, i+2), reflect.ValueOf(t).Elem().Field(j).Bool())
case "float32":
xlsx.SetCellValue("Sheet1", fmt.Sprintf("%s%d", column, i+2), reflect.ValueOf(t).Elem().Field(j).Float())
case "float64":
xlsx.SetCellValue("Sheet1", fmt.Sprintf("%s%d", column, i+2), reflect.ValueOf(t).Elem().Field(j).Float())
}
}
}

xlsx.SetActiveSheet(index)
// 保存到xlsx中
err := xlsx.SaveAs("test_write.xlsx")
if err != nil {
fmt.Println(err)
}
}

func main() {
var records []*Record\
records = append(records, &Record{\
Name: "小明",\
Age: 11,\
})\
records = append(records, &Record{\
Name: "小华",\
Age: 12,\
})\
// 反射写\
RefactorWrite(records)
}
https://learnku.com/articles/34635

Redis 解析工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
https://github.com/8090Lambert/go-redis-parser
go get github.com/8090Lambert/go-redis-parser
$ go-redis-parser -rdb <dump.rdb> -o <gen-file folder> -type <gen-file type, json or csv, default csv>

BigKeys 的输出示例:

# Scanning the rdb file to find biggest keys

-------- summary -------

Sampled 6 keys in the keyspace!
Total key length in bytes is 17

Biggest string found 's' has 1 bytes
Biggest hash found 'h' has 1 fields
Biggest list found 'li' has 2 items
Biggest sortedset found 'zset' has 2 members
Biggest set found 'set' has 2 members
Biggest stream found 'stream' has 3 entries

1 string with 1 bytes
1 hash with 1 fields
1 list with 2 items
https://learnku.com/articles/34655

批量导出 CSDN 博客并转为 hexo 博客风格

警惕 Go 编程陷阱

golang IoC

Golang 操作 Redis 的基本方法

Go Modules 不完全教程

Go 获取已插入 U 盘盘符

单元测试

一些例子和小项目

用Golang写爬虫(一)

go 版本的 requests

go命令行

go系列教程

beego 代码自动生成器

Go Modules 使用

文件传输管理系统Bigfile

基于 beego 的,有情怀的博客

Go 基础教程–2-基础知识

Go 语言 RESTful JSON API 创建

Golang slice 从源码来理解

golang gin + gorm + seesion 完成 Laravel 的 make:auth

Golang 学习宝库

学习go语言过程中写的一些例子和小项目

Go 实现常用设计模式(十)责任链模式

Go 语言命令概览

密码学之对称加密

golang算法与数据结构

用来展示 markdown 文档的博客

【GoLang 那点事】Go 指针

go语言中的接口

Go 语言面向对象特性

如何写出优雅的 Golang 代码

内部 API 的安全

Go 语言通道原理

Golang 写爬虫

Golang 新手教程视频

golang 对接支付宝支付

Go 语言入门

敏感词匹配

使用 go 的 gin 和 gorm 框架来构建 RESTful API 微服务

httprouter 源码分析

golang算法与数据结构:走迷宫-广度优先算法

Swoole 协程与 Go 协程的区别

程序员工作以后该如何提升

Go 开发基础入门

完整视频代码及学习资料-Zinx 框架-Golang 轻量级 TCP 并发服务

go基础库之解析以逗号分隔的数据

Golang(Go语言)简明教程

Golang+gin+vue+MySQL blog https:/www.iphpt.com

Go Modules 详解

markdown博客

YouTube download library and CLI written in Go

根据手机号识性别

Go 学习之路

Golang 中的面向对象继承

Laravel 博客替换成了 Go 写的博客

Go 语言 Excel 类库 Excelize

Golang 中的面向对象

httprouter 源码分析

Go for PHP Developers

golang 实现的泛型数组

go-Laravel-broadcast 实现 Laravel 的即时通讯

Golang 新手可能会踩的 50 个坑

使用 Go 语言的开源项目和公司

Go 如何实现 PHP 的密码加密解密

golang 实现斗地主棋牌游戏

Go 编写 Web 应用

模仿 Laravel 的项目结构和风格

HTTP 客户端

golang国内镜像

golang工具

Golang 使用 Map

奔跑的 Go

Go 如何实现 PHP 的密码加密解密

GOLANG 超大文件读取的两个方案

《 刻意学习 Golang - 标准库源码分析 》

专为微服务架构定制的高性能网关

go扫盲

对 Go 语言的综合评价

miniblink+golang 开发 Windows gui 应用

通过实例入门Golang

中文:文档首页

Go Modules 详解使用

Golang 的轻量级 TCP 并发服务器框架

Golang 入门指南

用 go 的 gin 和 gorm 框架来构建 RESTful API 微服务

go语言视频教程

Golang 新手教程:入门速成指南

Go 官方包的学习

Go 语言 Excel 文档excelize

Gin 学习示例代码

golang代理

go项目收集

Go语言标准库

手机归属地查询包

go beego学习

Go基础语法学习(1)