进度一
# 一、Go 基本语法
和 js,python 语法类似,具体见菜鸟教程,C 语言中文网 等,这里只记录难点
# (一) 数组
# 一维数组
初始化及遍历
1 2 3 4 5 6 7 8 9 10 11 var a [3 ]int fmt.Println(a[0 ]) fmt.Println(a[len (a)-1 ]) for i, v := range a { fmt.Printf("%d %d\n" , i, v) } for _, v := range a { fmt.Printf("%d\n" , v) }
在数组的定义中,如果在数组长度的位置出现 “…” 省略号,则表示数组的长度是根据初始化值的个数来计算,因此,上面数组 q 的定义可以简化为:
1 2 q := [...]int {1 , 2 , 3 } fmt.Printf("%T\n" , q)
# 循环遍历 range
1 2 3 4 5 6 slice := []int {10 , 20 , 30 , 40 } for index, value := range slice { fmt.Printf("Index: %d Value: %d\n" , index, value) }
切片
根据索引位置取切片 slice 元素值时,取值范围是(0~len (slice)-1),超界会报运行时错误,生成切片时,结束位置可以填写 len (slice) 但不会报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var highRiseBuilding [30 ]int for i := 0 ; i < 30 ; i++ { highRiseBuilding[i] = i + 1 } fmt.Println(highRiseBuilding[10 :15 ]) fmt.Println(highRiseBuilding[20 :]) fmt.Println(highRiseBuilding[:2 ])
结果:
1 2 3 [11 12 13 14 15 ] [21 22 23 24 25 26 27 28 29 30 ] [1 2 ]
动态创建切片,可以使用 make () 内建函数,格式如下:
1 make ( []Type, size, cap )
其中 Type 是指切片的元素类型,size 指的是为这个类型分配多少个元素,cap 为预分配的元素数量,这个值设定后不影响 size,只是能提前分配空间,降低多次分配空间造成的性能问题。
1 2 3 4 5 a := make ([]int , 2 ) b := make ([]int , 2 , 10 ) fmt.Println(a, b) fmt.Println(len (a), len (b))
结果:
其中 a 和 b 均是预分配 2 个元素的切片,只是 b 的内部存储空间已经分配了 10 个,但实际使用了 2 个元素。
容量不会影响当前的元素个数,因此 a 和 b 取 len 都是 2。
而为切片添加元素则与 python 类似,使用内建函数 append ()
1 2 3 4 var a []int a = append (a, 1 ) a = append (a, 1 , 2 , 3 ) a = append (a, []int {1 ,2 ,3 }...)
# 多维数组
声明:
1 2 3 4 5 6 7 8 var array [4 ][2 ]int array = [4 ][2 ]int {{10 , 11 }, {20 , 21 }, {30 , 31 }, {40 , 41 }} array = [4 ][2 ]int {1 : {20 , 21 }, 3 : {40 , 41 }} array = [4 ][2 ]int {1 : {0 : 20 }, 3 : {1 : 41 }}
# (二)函数
# 1. 声明
1 2 3 func 函数名(形式参数列表) (返回值列表){ 函数体 }
如:
1 2 3 4 func hypot (x, y float64 ) float64 { return math.Sqrt(x*x + y*y) } fmt.Println(hypot(3 ,4 ))
# 2. 调用
函数在定义后,可以通过调用的方式,让当前代码跳转到被调用的函数中进行执行,调用前的函数局部变量都会被保存起来不会丢失,被调用的函数运行结束后,恢复到调用函数的下一行继续执行代码,之前的局部变量也能继续访问。
函数内的局部变量只能在函数体中使用,函数调用结束后,这些局部变量都会被释放并且失效。
Go 语言的函数调用格式如下:
例如,加法函数调用样式如下:result := add (1,1)
# 3. 匿名函数
# 1) 在定义时调用
1 2 3 func (data int ) { fmt.Println("hello" , data) }(100 )
# 2) 赋值给变量
1 2 3 4 5 6 7 f := func (data int ) { fmt.Println("hello" , data) } f(100 )
# 3) 用作回调函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport ( "fmt" ) func visit (list []int , f func (int ) ) { for _, v := range list { f(v) } } func main () { visit([]int {1 , 2 , 3 , 4 }, func (v int ) { fmt.Println(v) }) }
# 结合命令行
将匿名函数作为 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 29 30 31 32 package mainimport ( "flag" "fmt" ) var skillParam = flag.String("skill" , "" , "skill to perform" )func main () { flag.Parse() var skill = map [string ]func () { "fire" : func () { fmt.Println("chicken fire" ) }, "run" : func () { fmt.Println("soldier run" ) }, "fly" : func () { fmt.Println("angel fly" ) }, } if f, ok := skill[*skillParam]; ok { f() } else { fmt.Println("skill not found" ) } }
代码说明如下:
第 8 行,定义命令行参数 skill,从命令行输入 --skill 可以将 =
后的字符串传入 skillParam 指针变量。
第 12 行,解析命令行参数,解析完成后,skillParam 指针变量将指向命令行传入的值。
第 14 行,定义一个从字符串映射到 func () 的 map,然后填充这个 map。
第 15~23 行,初始化 map 的键值对,值为匿名函数。
第 26 行,skillParam 是一个 *string 类型的指针变量,使用 *skillParam 获取到命令行传过来的值,并在 map 中查找对应命令行参数指定的字符串的函数。
第 29 行,如果在 map 定义中存在这个参数就调用,否则打印 “技能没有找到”。
运行代码,结果如下:
1 2 3 4 PS D:\code> go run main.go --skill=fly angel fly PS D:\code> go run main.go --skill=run soldier run
# (三) 接口调用
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 package mainimport ( "fmt" ) type Invoker interface { Call(interface {}) } type Struct struct {} func (s *Struct) Call(p interface {}) { fmt.Println("from struct" , p) } type FuncCaller func (interface {}) func (f FuncCaller) Call(p interface {}) { f(p) } func main () { var invoker Invoker s := new (Struct) invoker = s invoker.Call("hello" ) invoker = FuncCaller(func (v interface {}) { fmt.Println("from function" , v) }) invoker.Call("hello" ) }
# 二、一些 demo
# (一)go 读取 pdf 中的纯文本内容
# 1. 安装组件库
1 2 go get -u github.com/ledongthuc/pdfgo mod vendor
安装过程中报错:
1 connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
解决方法:
改成我们国内可用的代理地址,在命令提示符输入: go env -w GOPROXY=https://goproxy.cn , 然后再做各种操作就可以成功了。
# 2. 运行
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 mainimport ( "bytes" "fmt" "github.com/ledongthuc/pdf" ) func main () { pdf.DebugOn = true content, err := readPdf("test.pdf" ) if err != nil { panic (err) } fmt.Println(content) return } func readPdf (path string ) (string , error ) { f, r, err := pdf.Open(path) defer f.Close() if err != nil { return "" , err } var buf bytes.Buffer b, err := r.GetPlainText() if err != nil { return "" , err } buf.ReadFrom(b) return buf.String(), nil }
运行结果如下:
# (二) Go 连接数据库
安装依赖
1 go get -u github.com/go -sql-driver/mysql
# 1. go 连接 MySQL
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 package mainimport ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) func main () { dsn := "root:123456@tcp(127.0.0.1:3306)/testgo" db, err := sql.Open("mysql" , dsn) if err != nil { fmt.Printf("dsn:%s invalid,err:%v\n" , dsn, err) return } err = db.Ping() if err != nil { fmt.Printf("open %s faild,err:%v\n" , dsn, err) return } fmt.Println("连接数据库成功~" ) }
# 实现增删查改
首先在 mysql 的 testgo 中创建 user 表,实例有三个字段
1 2 3 4 5 6 CREATE TABLE `user`( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `name` VARCHAR(20) DEFAULT '', `age` INT(11) DEFAULT '0', PRIMARY KEY (`id`) )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
建表成功后如下:
# 增加 DB
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 package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) var db *sql.DB func initDB () (err error ) { dsn := "root:123456@tcp(127.0.0.1:3306)/testgo" db, err = sql.Open("mysql" , dsn) if err != nil { return } err = db.Ping() if err != nil { return } fmt.Println("连接数据库成功~" ) db.SetMaxIdleConns(10 ) return } func insert () { sqlStr := `insert into user(name,age) values("加油呀",28)` ret, err := db.Exec(sqlStr) if err != nil { fmt.Printf("insert failed,err:%v\n" , err) return } id, err := ret.LastInsertId() if err != nil { fmt.Printf("get id failed,err:%v\n" , err) return } fmt.Println("id" , id) } func main () { err := initDB() if err != nil { fmt.Printf("init DB failed,err%v\n" , err) } insert() }
# 删除
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 package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) var db *sql.DB func initDB () (err error ) { dsn := "root:123456@tcp(127.0.0.1:3306)/testgo" db, err = sql.Open("mysql" , dsn) if err != nil { return } err = db.Ping() if err != nil { return } fmt.Println("连接数据库成功~" ) db.SetMaxIdleConns(10 ) return } func deleteRow (id int ) { sqlStr := `delete from user where id=?` ret, err := db.Exec(sqlStr, id) if err != nil { fmt.Printf("delete faild,err:%v\n" , err) return } n, _ := ret.RowsAffected() fmt.Printf("删除了%d行数据\n" , n) } func main () { err := initDB() if err != nil { fmt.Printf("init DB failed,err%v\n" , err) } deleteRow(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 package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) var db *sql.DB func initDB () (err error ) { dsn := "root:123456@tcp(127.0.0.1:3306)/testgo" db, err = sql.Open("mysql" , dsn) if err != nil { return } err = db.Ping() if err != nil { return } fmt.Println("连接数据库成功~" ) db.SetMaxIdleConns(10 ) return } func updateRow (newAge int , id int ) { sqlStr := `update user set age=? where id=?` ret, err := db.Exec(sqlStr, newAge, id) if err != nil { fmt.Printf("update failed ,err:%v\n" , err) return } n, _ := ret.RowsAffected() fmt.Printf("更新了%d行数据\n" , n) } func main () { err := initDB() if err != nil { fmt.Printf("init DB failed,err%v\n" , err) } updateRow(20 , 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 72 73 74 75 package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) type user struct { id int name string age int } var db *sql.DB func initDB () (err error ) { dsn := "root:123456@tcp(127.0.0.1:3306)/testgo" db, err = sql.Open("mysql" , dsn) if err != nil { return } err = db.Ping() if err != nil { return } fmt.Println("连接数据库成功~" ) db.SetMaxIdleConns(10 ) return } func query (id int ) { sqlStr := "select id,name,age from user where id=?;" rowObj := db.QueryRow(sqlStr, id) var u1 user rowObj.Scan(&u1.id, &u1.name, &u1.age) fmt.Printf("u1:%#v\n" , u1) } func queryMore (n int ) { sqlStr := "select id,name,age from user where id >?;" rows, err := db.Query(sqlStr, n) if err != nil { fmt.Printf("%s query failed,err:%v\n" , sqlStr, err) return } defer rows.Close() for rows.Next() { var u1 user rows.Scan(&u1.id, &u1.name, &u1.age) fmt.Printf("u1:%#v\n" , u1) } } func main () { err := initDB() if err != nil { fmt.Printf("init DB failed,err%v\n" , err) } query(3 ) queryMore(0 ) }
结果实例如下:
# 2. GORM 连接数据库
# 2.1 下载依赖
1 go get -u github.com/jinzhu/gorm
# 2.2 连接数据库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" ) func main () { url := "root:123456@tcp(localhost:3306)/testgo?charset=utf8&parseTime=True&loc=Local" _, err := gorm.Open(mysql.Open(url), &gorm.Config{}) if err != nil { fmt.Println("连接失败" ) return } else { fmt.Println("连接成功" ) } }
报错按照 GoLand 提示做就行,下载相应依赖
# 2.3 根据 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 package mainimport ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" ) type User struct { UserId int `gorm:"primary_key"` UserName string UserSex string } func main () { dsn := "root:123456@tcp(localhost:3306)/testgo?charset=utf8&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { fmt.Println("数据库连接失败" ) return } db.AutoMigrate(&User{}) }
# 2.4 插入数据
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 mainimport ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" ) type User struct { UserId int `gorm:"primary_key"` UserName string UserSex string } func main () { dsn := "root:123456@tcp(localhost:3306)/testgo?charset=utf8&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { fmt.Println("连接数据库失败" ) return } user := User{1 , "张三" , "男" } db.Create(user) }
结果:
# 2.5 查询
First&Take&Last
1 2 3 4 5 6 7 #First():通过主键进行升序排列,获取第一条数据 select * from table order by id limit 1 #Task():不通过列进行排序,直接获取第一条数据 select * from table limit 1 #Last():通过主键进行降序排列,获取第一条数据 select * from table order by id desc limit 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 package mainimport ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" ) type Books struct { BookID int `gorm:"column:bookID",gorm:"primary_key"` BookName string `gorm:"column:bookName"` BookCounts int `gorm:"column:bookCounts"` Detail string `gorm:"column:detail"` } func main () { url := "root:123456@tcp(localhost:3306)/testgo?charset=utf8&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(url), &gorm.Config{}) if err != nil { fmt.Println("连接失败" ) return } var book Books db.First(&book) db.Last(&book) fmt.Println(book) }
# 2.6 多记录查询
1 2 books :=[]Books{} db.Find(&books)
# 2.7 条件查询
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 books := []Books{} db.Where("bookCounts between ? and ?" , 1 , 20 ).Find(&books) fmt.Println(books)
# 2.8 删除数据
1 2 3 4 5 6 db.Where("bookName = ?" , "Linux" ).Delete(Books{})
# 2.9 查询
1 2 3 4 5 6 7 8 9 db.Model(Books{}).Where("bookName" , "MySQL" ).Updates(map [string ]interface {}{"BookCounts" : "33" , "Detail" : "MySQL太难了" })
# 三、gin-vue-admin 项目学习
项目界面如下:
项目架构:
前端架构:
因为有前端 vue 基础,所以目前主要根据官方文档学后端 server 架构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ├── api │ └── v1 ├── config ├── core ├── docs ├── global ├── initialize │ └── internal ├── middleware ├── model │ ├── request │ └── response ├── packfile ├── resource │ ├── excel │ ├── page │ └── template ├── router ├── service ├── source └── utils ├── timer └── upload
# 四、go 代码审计
目前在跟 Firebasky 师傅的 demo 进行学习
# 附录:go 新手向
go 推荐项目
go 学习视频
Goweb 搭建 vue+go+gin+mysql+redis 的博客论坛
基于 gin 框架和 gorm 的 web 开发实战
# 参考文章
https://blog.csdn.net/memory_qianxiao/article/details/109632778
https://juejin.cn/post/7095205230983249934