完成以下问题, 表示你完成了一门编程语言的学习。
以下涉及了项目中常用的主题,可根据具体场景选择完成。

1.入门

1.1 官网地址,特性和应用场景有哪些

官网
社区
特性:简洁、快速、静态类型、富有表现力、并发性很好
场景:web服务、区块链、云服务、游戏后台等

Golang并不是严格面向对象,更多的是面向接口,所以数据类型不能用点的方式

1.2 安装与运行

参考 安装
下载安装即可
运行

  1. 创建工程

    1. // 创建目录
    2. mkdir hello
    3. cd hello
    4. // 创建mod文件,为工程创建包名
    5. go mod init example/hello
  2. 新建 main.go文件 ```go package main

import “fmt”

func main() { fmt.Println(“Hello, World!”) }

  1. 3. 运行
  2. `go run main.go`
  3. > 更多命令可参考 go help
  4. <a name="yGXyL"></a>
  5. ### 1.3 镜像(选答)
  6. ```c
  7. # 启用 Go Modules 功能
  8. go env -w GO111MODULE=on
  9. # 配置 GOPROXY 环境变量,以下三选一
  10. # 1. 七牛 CDN
  11. go env -w GOPROXY=https://goproxy.cn,direct
  12. # 2. 阿里云
  13. go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
  14. # 3. 官方
  15. go env -w GOPROXY=https://goproxy.io,direct

1.4 编辑器(选答)

VSCode、Goland

1.5 包管理工具

第三方库网站
安装、升级、卸载第三方库
只能安装和升级,不能卸载
升级和安装的方式一相同
有两种方式:

  1. // 方式一:在工程目录下执行
  2. go get -u github.com/gin-gonic/gin
  3. // 方式二:在代码中输入到包语句
  4. import "rsc.io/quote"
  5. // 然后执行
  6. go mod tidy
  7. // 如果克隆新工程,用第二种方式比较好

-u 表示自动更新包,而且当go get的时候会自动获取该包依赖的其他第三方包 使用 Go get 安装的第三方库会放到GOPATH目录的pkg/mod目录中

1.6 书籍推荐

《Go语言高级编程》
微信读书里有

2.概览

2.1 基本语法

  1. // 包名,一般和文件名相同
  2. package main
  3. // 导入包
  4. import "fmt"
  5. import "github.com/gin-gonic/gin"
  6. // 常量
  7. const (
  8. LiLy = 1
  9. )
  10. func main() {
  11. fmt.Println("Hello, World!")
  12. fmt.Println(LiLy)
  13. r := gin.Default()
  14. r.GET("/ping", func(c *gin.Context) {
  15. c.JSON(200, gin.H{
  16. "message": "pong",
  17. })
  18. })
  19. r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
  20. }
  21. // if
  22. if err := file.Chmod(0664); err != nil {
  23. log.Print(err)
  24. return err
  25. }
  26. // for
  27. sum := 0
  28. for i := 0; i < 10; i++ {
  29. sum += i
  30. }
  31. // 遍历键值对
  32. for key, value := range oldMap {
  33. newMap[key] = value
  34. }
  35. // 遍历数组
  36. sum := 0
  37. for _, value := range array {
  38. sum += value
  39. }
  40. // switch
  41. func shouldEscape(c byte) bool {
  42. switch c {
  43. case ' ', '?', '&', '=', '#', '+', '%':
  44. return true
  45. }
  46. return false
  47. }
  48. // 函数,可以返回多个参数
  49. // 小写字母开头的私有
  50. // 大写字母开头的公有
  51. func (file *File) Write(b []byte) (n int, err error) {
  52. return 0, err
  53. }
  54. // defer 在函数结束之前调用
  55. // 用于释放资源,使用栈保存,先进后出
  56. for i := 0; i < 5; i++ {
  57. defer fmt.Printf("%d ", i)
  58. }
  59. // 输出:4 3 2 1 0
  60. // panic
  61. // 类似抛出异常
  62. // 是一个内建函数,可以中断原有的控制流程,进入一个panic状态中。当函数F调用panic,函数F的执行被中断,但是F中的延迟函数会正常执行,然后F返回到调用它的地方。在调用的地方,F的行为就像调用了panic。这一过程继续向上,直到发生panic的goroutine中所有调用的函数返回,此时程序退出。panic可以直接调用panic产生。也可以由运行时错误产生,例如访问越界的数组。
  63. var user = os.Getenv("USER")
  64. func init() {
  65. if user == "" {
  66. panic("no value for $USER")
  67. }
  68. }
  69. // Recover
  70. // 是一个内建的函数,可以让进入panic状态的goroutine恢复过来。
  71. // recover仅在延迟函数中有效。在正常的执行过程中,调用recover会返回nil,并且没有其它任何效果。如果当前的goroutine陷入panic状态,调用recover可以捕获到panic的输入值,并且恢复正常的执行。
  72. func server(workChan <-chan *Work) {
  73. for work := range workChan {
  74. go safelyDo(work)
  75. }
  76. }
  77. func safelyDo(work *Work) {
  78. defer func() {
  79. if err := recover(); err != nil {
  80. log.Println("work failed:", err)
  81. }
  82. }()
  83. do(work)
  84. }

2.2 面向对象

  1. // 包名,一般和文件名相同
  2. package main
  3. // 导入包
  4. import "fmt"
  5. func main() {
  6. p := Person{name: "Ren", age: 30}
  7. p.run()
  8. }
  9. // 结构体
  10. type Person struct {
  11. name string
  12. age int
  13. }
  14. func (p Person) run() {
  15. s := fmt.Sprintf("%s who is %d years old running", p.name, p.age)
  16. fmt.Println(s)
  17. }
  18. // 继承
  19. // 通过匿名字段的方式, 当匿名字段是一个struct的时候,那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct。
  20. // 当然也可以重写方法
  21. package main
  22. import "fmt"
  23. type Skills []string
  24. type Human struct {
  25. name string
  26. age int
  27. weight int
  28. }
  29. type Student struct {
  30. Human // 匿名字段,struct
  31. Skills // 匿名字段,自定义的类型string slice
  32. int // 内置类型作为匿名字段
  33. speciality string
  34. }
  35. func main() {
  36. // 初始化学生Jane
  37. jane := Student{Human:Human{"Jane", 35, 100}, speciality:"Biology"}
  38. // 现在我们来访问相应的字段
  39. fmt.Println("Her name is ", jane.name)
  40. fmt.Println("Her age is ", jane.age)
  41. fmt.Println("Her weight is ", jane.weight)
  42. fmt.Println("Her speciality is ", jane.speciality)
  43. // 我们来修改他的skill技能字段
  44. jane.Skills = []string{"anatomy"}
  45. fmt.Println("Her skills are ", jane.Skills)
  46. fmt.Println("She acquired two new ones ")
  47. jane.Skills = append(jane.Skills, "physics", "golang")
  48. fmt.Println("Her skills now are ", jane.Skills)
  49. // 修改匿名内置类型字段
  50. jane.int = 3
  51. fmt.Println("Her preferred number is", jane.int)
  52. }

2.3 接口

不需要显示说明实现了某个接口,它没有继承或子类或“implements”关键字,只是通过约定的形式,隐式的实现interface 中的方法即可
会在编译时期检查是否实现

  1. // 定义一个接口
  2. type Man interface {
  3. sayHello(name string)
  4. }
  5. type Human struct {
  6. name string
  7. age int
  8. phone string
  9. }
  10. // 隐式实现
  11. // 至于为什么,更多的可能是简洁
  12. func (h Human) sayHello(name string) {
  13. fmt.Printf("Hello %s, this is %s", name, h.name)
  14. }

2.4 并发

goroutine
Go并行设计的核心。goroutine说到底其实就是协程,但是它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。
goroutine通过 go 关键字实现。
设计原则:不要通过共享来通信,而要通过通信来共享。

  1. go hello(a, b, c)

channels
用于goroutine之间数据的通信。channel可以与Unix shell 中的双向管道做类比:可以通过它发送或者接收值。

  1. // 创建
  2. ci := make(chan int)
  3. cs := make(chan string)
  4. cf := make(chan interface{})
  5. // 第二个参数表示缓冲数量
  6. ch := make(chan string, 5)

channel通过操作符 <- 来接收和发送数据

  1. ch <- v // 发送v到channel ch.
  2. v := <-ch // 从ch中接收数据,并赋值给v

示例:
单次获取channel

  1. package main
  2. import "fmt"
  3. func sum(a []int, c chan int) {
  4. total := 0
  5. for _, v := range a {
  6. total += v
  7. }
  8. c <- total // send total to c
  9. }
  10. func main() {
  11. a := []int{7, 2, 8, -9, 4, 0}
  12. // 必须使用 make 创建channel
  13. c := make(chan int)
  14. go sum(a[:len(a)/2], c)
  15. go sum(a[len(a)/2:], c)
  16. x, y := <-c, <-c // receive from c
  17. fmt.Println(x, y, x + y)
  18. }

循环读取channel

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func fibonacci(n int, c chan int) {
  6. x, y := 1, 1
  7. for i := 0; i < n; i++ {
  8. c <- x
  9. x, y = y, x + y
  10. }
  11. // 关闭channel
  12. // 应该在生产者的地方关闭channel
  13. close(c)
  14. }
  15. func main() {
  16. c := make(chan int, 10)
  17. // 这里注意缓冲是10,发送不能超过10次
  18. go fibonacci(cap(c), c)
  19. for i := range c {
  20. fmt.Println(i)
  21. }
  22. }

循环读取多个channel

  1. package main
  2. import "fmt"
  3. func fibonacci(c, quit chan int) {
  4. x, y := 1, 1
  5. for {
  6. select {
  7. case c <- x:
  8. x, y = y, x + y
  9. case <-quit:
  10. fmt.Println("quit")
  11. return
  12. }
  13. }
  14. }
  15. func main() {
  16. c := make(chan int)
  17. quit := make(chan int)
  18. go func() {
  19. for i := 0; i < 10; i++ {
  20. fmt.Println(<-c)
  21. }
  22. quit <- 0
  23. }()
  24. fibonacci(c, quit)
  25. }

2.5 测试

  1. 创建以 _test.go 结尾的文件;
  2. 导入 testing 包;
  3. 创建以 Test 开头的测试方法;
  4. 执行 go test

举例:

  1. import (
  2. "testing"
  3. "regexp"
  4. )
  5. // TestHelloName calls greetings.Hello with a name, checking
  6. // for a valid return value.
  7. func TestHelloName(t *testing.T) {
  8. name := "Gladys"
  9. want := regexp.MustCompile(`\b`+name+`\b`)
  10. msg, err := Hello("Gladys")
  11. if !want.MatchString(msg) || err != nil {
  12. // 如果有错误就报错,并没有提供对比的方法
  13. t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
  14. }
  15. }

2.6 make、new

  • new 返回类型零值的指针
  • make用于内建类型(map、slice 和channel)的内存分配,而且只能创建这三种类型,make返回初始化后的(非零)值

原因:本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。我理解为这三种类型是封装类型,必须被初始化,又不想总是写构造函数,所以新增了make关键字。

2.7 Module

待补充

3.数据类型

3.1 数字

  1. // 包名,一般和文件名相同
  2. package main
  3. // 导入包
  4. import "fmt"
  5. import "math"
  6. func main() {
  7. // 整数,加减乘除余
  8. var x int = 3
  9. var y int = 5
  10. fmt.Println("x =", x)
  11. fmt.Println("y =", y)
  12. fmt.Println("x + y =", x + y)
  13. fmt.Println("x - y =", x - y)
  14. fmt.Println("x * y =", x * y)
  15. fmt.Println("x / y =", x / y)
  16. fmt.Println("x % y =", x % y)
  17. // 小数点后两位,四舍六入
  18. // 输出 2.68
  19. var z float64 = 2.6789124
  20. fmt.Println(fmt.Sprintf("%.2f", z))
  21. // 转为整数,去掉小数部分
  22. fmt.Println("z =", int(z))
  23. // 指数
  24. p := math.Pow(2, 3)
  25. fmt.Println("p=", p)
  26. // 接口转数字,转其他的类型也类似
  27. var a interface{}
  28. anum := a.(int)
  29. }

3.2 字符

  1. func str() {
  2. var str string = "abcdefg"
  3. // 格式化
  4. var s string = fmt.Sprintf("my name is %s", str)
  5. fmt.Println(s)
  6. // 拼接
  7. fmt.Println(str + "ijk")
  8. // 截取
  9. fmt.Println(str[1:])
  10. fmt.Println(str[:len(str)-3])
  11. // 长度
  12. fmt.Println("str length =", len(str))
  13. // 遍历
  14. for _, v := range str {
  15. // v 是ASCII码数值,需要转为字符串
  16. fmt.Println("char =", string(v))
  17. }
  18. // 分割
  19. arr := strings.Split(str, "c")
  20. fmt.Println(arr)
  21. // 替换
  22. rep := strings.ReplaceAll(str, "a", "x")
  23. fmt.Println(rep)
  24. // 正则表达式
  25. reg, err := regexp.Compile("H.*d")
  26. if err != nil {
  27. panic(err)
  28. }
  29. // 是否包含
  30. b := reg.MatchString("aHellod")
  31. fmt.Println(b)
  32. // 返回匹配的子串
  33. a := reg.FindString("adbHzd")
  34. fmt.Println(a)
  35. }

3.3 列表

数组

  • 数组不能改变长度
  • 数组之间的赋值是值的赋值,传入的其实是该数组的副本,而不是它的指针
  • 用得更多的是slice切片,即动态数组 ```go

var arr [10]int // 声明了一个int类型的数组,默认都是0 arr[0] = 42 // 数组下标是从0开始的 arr[1] = 13 // 赋值操作 // 遍历 for _, v := range arr { fmt.Println(v) } a := [3]int{1, 2, 3} // 声明了一个长度为3的int数组 b := [10]int{1, 2, 3} // 声明了一个长度为10的int数组,其中前三个元素初始化为1、2、3,其它默认为0 c := […]int{4, 5, 6} // 可以省略长度而采用...的方式,Go会自动根据元素个数来计算长度

  1. **slice(动态数组)**<br />slice总是指向一个底层arrayslice的声明也可以像array一样,只是不需要长度<br />slice是引用类型,所以当引用改变其中元素的值时,其它的所有引用都会改变该值,比如a变了,b也会变<br />slice包含了三个元素
  2. - 一个指针,指向数组中slice指定的开始位置
  3. - 长度length,即slice的长度
  4. - 最大长度cap,也就是slice开始位置到数组的最后位置的长度
  5. ```go
  6. // 定义
  7. // 默认长度和容量都是0
  8. var fslice []int
  9. // 或者使用make,指定其长度为 3 个元素,容量为 5 个元素
  10. // 默认值都是0
  11. slice := make([]int, 3, 5)
  12. // 创建字符串切片
  13. // 其长度和容量都是 3 个元素
  14. myStr := []string{"Jack", "Mark", "Nick"}

操作

  1. package main
  2. // 导入包
  3. import (
  4. "fmt"
  5. "sort" // sort包中的Slice方法可以排序任何类型
  6. )
  7. func main() {
  8. // golang 对 slice 的操作全在 [:] 中 和 append
  9. // 所以很多操作需要自己写
  10. // 关于 [:] 操作,和 Python 一样,左包含右不包含
  11. arr := make([]int, 2, 10)
  12. printSlice(arr)
  13. // 新增
  14. // 头部插入
  15. arr = append([]int{1}, arr...)
  16. printSlice(arr)
  17. // 尾部插入
  18. arr = append(arr, 2)
  19. printSlice(arr)
  20. // 任意位置插入
  21. // 官方没有提供现成的函数,必须创建一个新的切片
  22. i := 2
  23. newArr := make([]int, len(arr)+1)
  24. copy(newArr[:i], arr[:i])
  25. newArr[i] = 3
  26. copy(newArr[i+1:], arr[i:])
  27. printSlice(newArr)
  28. // 删除
  29. // 按位置
  30. p := 3
  31. newArr = append(newArr[:p-1], newArr[p:]...)
  32. printSlice(newArr)
  33. // 按元素
  34. newArr = delItem(newArr, 2)
  35. printSlice(newArr)
  36. // 修改
  37. // 按位置
  38. newArr[1] = 2
  39. printSlice(newArr)
  40. // 包含
  41. c := contains(newArr, 1)
  42. fmt.Println(c)
  43. // 排序,使用sort包
  44. sort.Slice(newArr, func(i, j int) bool {
  45. return newArr[i] < newArr[j]
  46. })
  47. printSlice(newArr)
  48. }
  49. // 包含,可以用二分法查找
  50. func contains(arr []int, s int) bool {
  51. for _, v := range arr {
  52. if v == s {
  53. return true
  54. }
  55. }
  56. return false
  57. }
  58. func delItem(vs []int, s int) []int {
  59. // 也可以正向
  60. maxIdx := len(vs) - 1
  61. for i := maxIdx; i >= 0; i-- {
  62. if s == vs[i] {
  63. vs = append(vs[:i], vs[i+1:]...)
  64. }
  65. }
  66. return vs
  67. }
  68. func printSlice(arr []int) {
  69. fmt.Println(arr)
  70. }

3.4 字典(键值对)

其底层存储方式为数组,在存储时key不能重复,当key重复时,value进行覆盖,我们通过key进行hash运算(可以简单理解为把key转化为一个整形数字)然后对数组的长度取余,得到key存储在数组的哪个下标位置,最后将key和value组装为一个结构体,放入数组下标处
hash冲突的常见解决方法
线性探测,字面意思就是按照顺序来,从冲突的下标处开始往后探测,到达数组末尾时,从数组开始处探测,直到找到一个空位置存储这个key,当数组都找不到的情况下回扩容
拉链,简单理解为链表,当key的hash冲突时,我们在冲突位置的元素上形成一个链表,通过指针互连接,当查找时,发现key冲突,顺着链表一直往下找,直到链表的尾节点

  1. package main
  2. import "fmt"
  3. func main() {
  4. fmt.Printf("%s\n", "hello world")
  5. // 声明
  6. numbers := make(map[string]int)
  7. numbers["one"] = 1 //赋值
  8. numbers["ten"] = 10 //赋值
  9. numbers["three"] = 3
  10. fmt.Println(numbers) // 读取数据
  11. // 声明与赋值
  12. rating := map[string]float32{"C":5, "Go":4.5, "Python":4.5, "C++":2 }
  13. fmt.Println(rating)
  14. // 新增
  15. rating["Java"] = 6
  16. fmt.Println(rating)
  17. // 删除
  18. delete(rating, "Java")
  19. fmt.Println(rating)
  20. // 包含
  21. if _, ok := rating["Python"]; ok {
  22. fmt.Println("key is Python, value is", rating["Python"])
  23. }
  24. // 遍历
  25. for key, value := range rating {
  26. s := fmt.Sprintf("key is %s, value is %.2f", key, value)
  27. fmt.Println(s)
  28. }
  29. // 排序
  30. // 只能遍历后将键或者值放slice里再排序
  31. }

没有提供集合的数据结构

3.5 栈和队列

参考数据结构

4.练习

4.1 时间

  1. package main
  2. // 导入包
  3. import (
  4. "fmt"
  5. "time"
  6. )
  7. const TimeFormat = "2006-01-02 15:04:05"
  8. func main() {
  9. // 当前时间戳
  10. timeStep := time.Now().Unix()
  11. fmt.Println("时间戳:", timeStep)
  12. // 格式化
  13. now := time.Now().Format(TimeFormat)
  14. fmt.Println("当前时间:", now)
  15. // 格式化时间转时间戳
  16. t, err := time.Parse(TimeFormat, now)
  17. if err != nil {
  18. fmt.Println(err)
  19. }
  20. fmt.Println("格式化时间转时间戳", t.Unix())
  21. // 时间戳转格式化时间
  22. t1 := time.Unix(timeStep, 0)
  23. fmt.Println("时间戳转格式化时间:", t1.Format(TimeFormat))
  24. }

4.2 文件与目录

4.2.1 文件

只要获取文件对象,就可以做操作了
自定义长度

  1. // os.open
  2. f, err := os.OpenFile("example.py", os.O_RDWR, 0666)
  3. if err != nil {
  4. fmt.Println(err)
  5. return
  6. }
  7. defer f.Close()
  8. const size int = 128
  9. var b [size]byte
  10. for {
  11. n ,err := f.Read(b[:])
  12. if err != nil {
  13. fmt.Println("read file err:", err.Error())
  14. break
  15. }
  16. fmt.Printf("读到了%d个字节:%s\n", n, string(b[0:n]))
  17. if n < size {
  18. break
  19. }
  20. }

按行

  1. // bufio.ReadString
  2. f, err := os.Open("example.py")
  3. if err != nil {
  4. fmt.Println("errors=", err.Error())
  5. return
  6. }
  7. defer f.Close()
  8. buff := bufio.NewReader(f)
  9. for {
  10. // 这里的参数是byte类型,所以用单引号
  11. line, err := buff.ReadString('\n')
  12. if err == io.EOF {
  13. fmt.Println("read file EOF")
  14. break
  15. }
  16. if err != nil {
  17. fmt.Println("err=", err)
  18. }
  19. fmt.Println("line=", line)
  20. }

全部读取

  1. ll, err := ioutil.ReadFile("example.py")
  2. if err != nil {
  3. fmt.Println(err)
  4. return
  5. }
  6. fmt.Println(string(all))

写入

  1. // 其实读文件不过是该函数指定的只读模式 penFile(name, O_RDONLY, 0)
  2. f, err := os.OpenFile("a.log", os.O_APPEND | os.O_RDWR | os.O_CREATE, 0644)
  3. if err != nil {
  4. fmt.Println(err)
  5. return
  6. }
  7. defer f.Close()
  8. f.WriteString(time.Now().String() + "\n")
  9. // 或者使用缓存
  10. writer := bufio.NewWriter(f)
  11. writer.WriteString("write buffer")
  12. writer.Flush()

其他操作

  1. // 文件大小
  2. fileInfo, err := os.Stat("hello_1_test.go")
  3. if err != nil {
  4. fmt.Println(err)
  5. }
  6. fmt.Println(fileInfo.Size())
  7. // 删除文件
  8. if err:=os.Remove("hello_test.go");err!=nil {
  9. fmt.Println(err)
  10. }
  11. // 修改文件名
  12. if err:=os.Rename("hello_test.go", "hello_1_test.go");err!=nil {
  13. fmt.Println(err)
  14. }

4.2.2 目录

  1. // 当前工作目录
  2. cwd, err := os.Getwd()
  3. if err != nil {
  4. fmt.Println(err)
  5. }
  6. fmt.Println(cwd)
  7. // 目录操作用 "path/filepath" 库
  8. // 列出文件与目录
  9. err = filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
  10. fmt.Println("path=", path)
  11. fmt.Println("name=", info.Name())
  12. return nil
  13. })
  14. if err != nil {
  15. fmt.Println(err)
  16. }
  17. // 绝对路径
  18. // Getwd() 返回的也是绝对路径
  19. abs, err := filepath.Abs("assert/os.txt")
  20. if err != nil {
  21. fmt.Println(err)
  22. }
  23. // 删除目录
  24. if err:=os.RemoveAll("assert");err!=nil {
  25. fmt.Println(err)
  26. }
  27. // 拼接路径
  28. osText := filepath.Join(cwd, "assert", "os.txt")
  29. // 分离路径
  30. cwdDir := filepath.Dir(osText)
  31. fmt.Println(cwdDir)
  32. // 分离文件名
  33. baseName := filepath.Base(osText)
  34. fmt.Println(baseName)
  35. // 后缀名
  36. extName := filepath.Ext(baseName)
  37. fmt.Println(extName)
  38. // 同时获得路径与文件名
  39. dir, file := filepath.Split(osText)
  40. fmt.Println(dir, "\t", file)
  41. // 是否存在
  42. // 这里使用了 nil 和 IsNotExist 双重判断
  43. func PathExists(path string) (bool, error) {
  44. _, err := os.Stat(path)
  45. if err == nil {
  46. return true, nil
  47. }
  48. // 这里其实是判断错误类型是不是 ErrNotExist
  49. if os.IsNotExist(err) {
  50. return false, nil
  51. }
  52. //这个函数是判断错误类型是不是 ErrExist
  53. //os.IsExist(err)
  54. return false, err
  55. }
  56. // 复制文件
  57. // 也可以全部读再全部写,使用 ioutil 包,但是对内存有要求
  58. // 性能上差距不大
  59. func copyFile2(srcFile, destFile string)(int64,error){
  60. file1,err:=os.Open(srcFile)
  61. if err != nil{
  62. return 0,err
  63. }
  64. file2,err:=os.OpenFile(destFile,os.O_WRONLY|os.O_CREATE,os.ModePerm)
  65. if err !=nil{
  66. return 0,err
  67. }
  68. defer file1.Close()
  69. defer file2.Close()
  70. // 会创建缓冲区
  71. return io.Copy(file2,file1)
  72. }
  73. // 删除目录
  74. os.RemoveAll("path")

4.3 加密

MD5加密, SHA1的用法是一样的

  1. package main
  2. import (
  3. "crypto/md5"
  4. "fmt"
  5. "io"
  6. "log"
  7. "os"
  8. )
  9. func main() {
  10. // 字符串
  11. data := []byte("These pretzels are making me thirsty.")
  12. fmt.Printf("%x\n", md5.Sum(data))
  13. // 文件
  14. f, err := os.Open("hello_1_test.go")
  15. if err != nil {
  16. log.Fatal(err)
  17. }
  18. defer f.Close()
  19. h := md5.New()
  20. if _, err := io.Copy(h, f); err != nil {
  21. log.Fatal(err)
  22. }
  23. fmt.Printf("%x", h.Sum(nil))
  24. }

4.4 网络服务

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. "time"
  6. )
  7. func greet(w http.ResponseWriter, r *http.Request) {
  8. fmt.Fprintf(w, "Hello World! %s", time.Now())
  9. }
  10. func main() {
  11. http.HandleFunc("/", greet)
  12. http.ListenAndServe(":8080", nil) // 第二个参数是路由对象,不传则表示使用http包默认的路由器
  13. }

4.5 数据库操作

  1. package main
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "github.com/go-sql-driver/mysql" // 数据库驱动
  6. "log"
  7. "os"
  8. )
  9. // 连接后的对象
  10. var db *sql.DB
  11. type Album struct {
  12. ID int64
  13. Title string
  14. Artist string
  15. Price float32
  16. }
  17. // albumsByArtist queries for albums that have the specified artist name.
  18. func albumsByArtist(name string) ([]Album, error) {
  19. // An albums slice to hold data from returned rows.
  20. var albums []Album
  21. rows, err := db.Query("SELECT * FROM album WHERE artist = ?", name)
  22. if err != nil {
  23. return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
  24. }
  25. defer rows.Close()
  26. // Loop through rows, using Scan to assign column data to struct fields.
  27. for rows.Next() {
  28. var alb Album
  29. if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
  30. return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
  31. }
  32. albums = append(albums, alb)
  33. }
  34. if err := rows.Err(); err != nil {
  35. return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
  36. }
  37. return albums, nil
  38. }
  39. // albumByID queries for the album with the specified ID.
  40. func albumByID(id int64) (Album, error) {
  41. // An album to hold data from the returned row.
  42. var alb Album
  43. row := db.QueryRow("SELECT * FROM album WHERE id = ?", id)
  44. if err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
  45. if err == sql.ErrNoRows {
  46. return alb, fmt.Errorf("albumsById %d: no such album", id)
  47. }
  48. return alb, fmt.Errorf("albumsById %d: %v", id, err)
  49. }
  50. return alb, nil
  51. }
  52. // addAlbum adds the specified album to the database,
  53. // returning the album ID of the new entry
  54. func addAlbum(alb Album) (int64, error) {
  55. result, err := db.Exec("INSERT INTO album (title, artist, price) VALUES (?, ?, ?)", alb.Title, alb.Artist, alb.Price)
  56. if err != nil {
  57. return 0, fmt.Errorf("addAlbum: %v", err)
  58. }
  59. id, err := result.LastInsertId()
  60. if err != nil {
  61. return 0, fmt.Errorf("addAlbum: %v", err)
  62. }
  63. return id, nil
  64. }
  65. func main() {
  66. // Capture connection properties.
  67. cfg := mysql.Config{
  68. User: os.Getenv("DBUSER"),
  69. Passwd: os.Getenv("DBPASS"),
  70. Net: "tcp",
  71. Addr: "127.0.0.1:33306",
  72. DBName: "recordings",
  73. }
  74. // Get a database handle.
  75. var err error
  76. db, err = sql.Open("mysql", cfg.FormatDSN())
  77. if err != nil {
  78. log.Fatal(err)
  79. }
  80. pingErr := db.Ping()
  81. if pingErr != nil {
  82. log.Fatal(pingErr)
  83. }
  84. fmt.Println("Connected!")
  85. albums, err := albumsByArtist("John Coltrane")
  86. if err != nil {
  87. log.Fatal(err)
  88. }
  89. fmt.Printf("Albums found: %v\n", albums)
  90. // Hard-code ID 2 here to test the query.
  91. alb, err := albumByID(2)
  92. if err != nil {
  93. log.Fatal(err)
  94. }
  95. fmt.Printf("Album found: %v\n", alb)
  96. albID, err := addAlbum(Album{
  97. Title: "The Modern Sound of Betty Carter",
  98. Artist: "Betty Carter",
  99. Price: 49.99,
  100. })
  101. if err != nil {
  102. log.Fatal(err)
  103. }
  104. fmt.Printf("ID of added album: %v\n", albID)
  105. }

4.6 数据处理

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. // 如果字段名不一致则可以使用注解
  7. type Dog struct {
  8. ID int `json:"id"`
  9. Name string `json:"name"`
  10. }
  11. func main() {
  12. // object -> json
  13. d1 := Dog{ID: 1, Name: "Petter"}
  14. pb, err := json.MarshalIndent(d1, "", " ")
  15. if err != nil {
  16. fmt.Println("marshl err=", err.Error())
  17. return
  18. }
  19. fmt.Println(string(pb))
  20. // json -> object
  21. var d2 Dog
  22. myDog := `{"id":2, "Name":"mydog"}`
  23. err = json.Unmarshal([]byte(myDog), &d2)
  24. if err != nil {
  25. fmt.Println(err)
  26. return
  27. }
  28. fmt.Println(d2)
  29. }

5 项目

Web框架、rom框架

5.1 Gin 框架

官网
Golang - 图1

5.2 GORM 框架

待补充