Go语言上手 | 青训营笔记

113 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记

1 GO语言上手

1.1 什么是go语言

  1. 高性能、高并发

    • 内建高并发编程
    • 不需要第三方库,只需要标准库或者基于标准库
  2. 语法简单、学习曲线平缓

    • 类似C语言,但是大量简化
  3. 丰富的标准库

    • 和python一样,有很高稳定性、兼容性
  4. 完善的工具链

    • 编译、代码格式化、错误检查、帮助文档、包管理、代码补充提示
    • 内置完整单元测试框架,单元测试、性能测试、代码覆盖率、数值竞争检测、性能优化
  5. 静态链接

    • 所有编译结构默认静态链接、拷贝编译之后唯一一个可执行文件,不需要附加
    • 线上容器环境下运行,镜像控制的非常小、部署方便快捷
  6. 快速编译

  7. 跨平台

    • 交叉编译,无需配置环境
  8. 垃圾回收

    • 无需考虑内存的分配释放,专注业务逻辑

2.2 基础语法

helloworld

package main

import (
	"fmt"
)

func main() {
	fmt.Println("hello world")
}
  • package main:文件属于main的一部分,main包也就是程序入口包
  • import:导入包
  • fmt:屏幕输入输出字符串、格式化字符串

变量

package main

import (
	"fmt"
	"math"
)
func main() {

	var a = "initial"
	var b, c int = 1, 2 //自己写变量类型
	var d = true  
	var e float64

	f := float32(e)  //一种
	g := a + "foo"
	fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0
	fmt.Println(g)                // initialapple
	const s string = "constant"
	const h = 500000000
	const i = 3e20 / h
	fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}
  • 强类型语言,跟python一样,可以先声明类型
  • 字符串可以加号拼接、=比较
  • const为常量,没有确定的类型(自己决定),不可更改

if-else

package main

import "fmt"

func main() {

	if 7%2 == 0 {
		fmt.Println("7 is even")
	} else {
		fmt.Println("7 is odd")
	}

	if 8%4 == 0 {
		fmt.Println("8 is divisible by 4")
	}

	if num := 9; num < 0 {
		fmt.Println(num, "is negative")
	} else if num < 10 {
		fmt.Println(num, "has 1 digit")
	} else {
		fmt.Println(num, "has multiple digits")
	}
}
  • if没有括号
  • num:=9 等于var num=9

循环

  • 只有for循环,空为死循环
  • 经典:for i:=1 ; i<10; i++

switch

package main

import (
	"fmt"
	"time"
)

func main() {

	a := 2
	switch a {
	case 1:
		fmt.Println("one")
	case 2:
		fmt.Println("two")
	case 3:
		fmt.Println("three")
	case 4, 5:
		fmt.Println("four or five")
	default:
		fmt.Println("other")
	}

	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("It's before noon")
	default:
		fmt.Println("It's after noon")
	}
}
  • switch变量不需要括号,可以使用任意一种类型变量
  • 不需要加break也可以自己跳出去

数组

  • 数组长度固定、切片更实用
  • b := [5]int{1, 2, 3, 4, 5}
  • var twoD [2][3]int
  • var a [5]int

切片

  • 可变长度
  • s := make([]string, 3),长度可变
  • append(s,”s“)追加元素
  • copy复制切片
  • len(s)长度
  • 和python一样的切片操作

map

  • m := make(map[string]int)
  • r, ok := m["unknow"]
  • fmt.Println(r, ok) // 0 false map是不是有key存在
  • 随机顺序

range

  • 数组快速索引,第一个数组为索引,不想用的话可以和python一样用下划线
  • for i, num := range nums {
  • for k, v := range m {
  • 遍历map第一个值为key,第二个为value

函数

  • 天生支持返回多个值
  • func add(a int, b int) int {
  • 括号内为局部变量参数、后面一个是返回数值类型,支持多个
  • func exists(m map[string]string, k string) (v string, ok bool) {

指针

  • 作用:对传入的参数进行修改
package main

import "fmt"

func add2(n int) {  //没有意义,传入的参数是一个拷贝
   n += 2
}

func add2ptr(n *int) {  //加*
   *n += 2
}

func main() {
   n := 5
   add2(n)
   fmt.Println(n) // 5
   add2ptr(&n) //C语言
   fmt.Println(n) // 7
}

结构体

  • 带类型的字段的集合
  • 没有初始化的为空值
  • 指针可以对结构体进行修改
package main

import "fmt"

type user struct {
   name     string
   password string
}

func main() {
   a := user{name: "wang", password: "1024"}
   b := user{"wang", "1024"}
   c := user{name: "wang"}
   c.password = "1024"
   var d user
   d.name = "wang"
   d.password = "1024"

   fmt.Println(a, b, c, d)                 // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
   fmt.Println(checkPassword(a, "haha"))   // false
   fmt.Println(checkPassword2(&a, "haha")) // false
}

func checkPassword(u user, password string) bool {
   return u.password == password
}

func checkPassword2(u *user, password string) bool {
   return u.password == password
}

结构体方法

package main

import "fmt"

type user struct {
   name     string
   password string
}

func (u user) checkPassword(password string) bool {
   return u.password == password
}

func (u *user) resetPassword(password string) {
   u.password = password
}

func main() {
   a := user{name: "wang", password: "1024"}
   a.resetPassword("2048")
   fmt.Println(a.checkPassword("2048")) // true
}
  • func后面加(变量名,结构体类型)
  • 需要修改的话,结构体类型要变为指针(前面加*)

错误处理

package main

import (
   "errors"
   "fmt"
)

type user struct {
   name     string
   password string
}

func findUser(users []user, name string) (v *user, err error) {
   for _, u := range users {
      if u.name == name {
         return &u, nil
      }
   }
   return nil, errors.New("not found")
}

func main() {
   u, err := findUser([]user{{"wang", "1024"}}, "wang")
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(u.name) // wang

   if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
      fmt.Println(err) // not found
      return
   } else {
      fmt.Println(u.name)
   }
}
  • 返回值加上error
  • 没错返回nil,错误返回errors.New("not found")
  • u, err := findUser([]user{{"wang", "1024"}}, "wang")

字符串操作

  • strings.Contains(a, "ll") 字符串是否包括ll
  • strings.Count(a, "l") 字符串l的数量
  • strings.HasPrefix(a, "he") 判断字符串s是否以prefix开头
  • strings.HasSuffix(a, "llo") 判断字符串s是否以suffix结尾
  • strings.Index(a, "ll") 字符串ll所在的起始位置
  • strings.Join([]string{"he", "llo"}, "-") 字符串用-组合
  • strings.Repeat(a, 2) 字符串重复
  • strings.Replace(a, "e", "E", -1) 字符串前n个e替换为E,-1代表没有限制
  • strings.Split("a-b-c", "-") 字符串按照-分割
  • strings.ToLower(a) 字符串全小写
  • strings.ToUpper(a) 字符串全大写
  • len(a) 字符串长度
  • ReverseStr(str1) 字符串反转

字符串格式化

  • 可以用%v任意打印
  • +v可以更详细的打印字段的名字,值
  • #v可以打印出字段的名称和值(包括结构体的结构和名称)
  • fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
  • fmt.Printf("%.2f\n", f) // 3.14 .f保留小数点两位浮点数

JSON处理

  • 结构体需要字段名的第一个字母大写

  • buf, err := json.Marshal(a) 对结构体json序列化,会变成一个字符串数组(string类型转换)

  • buf, err = json.MarshalIndent(a, "", "\t") Marshal的基础上****前缀不为空,****Indent()对读的结果做了一些处理,简单说就是对Json 多了一些格式处理。

  • err = json.Unmarshal(buf, &b) json反序列化到结构体(注意结构体变量要取地址)

时间处理

  • time.Now() 快速获取当前时间
  • t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC) 构造时间
  • t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute() 取参数
  • t.Format("2006-01-02 15:04:05") 按照所需格式进行标准化
  • t2.Sub(t) 返回两个时间相差的时间段*。*
  • time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
  • now.Unix() 时间戳
  • time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36") 字符串解析成时间

数字解析

  • strconv.ParseInt(字符串,进制(0为10),返回的数值精度)
  • f, _ := strconv.ParseFloat(字符串, 返回的数值精度)
  • strconv.Atoi("123") 快速把十进制字符串转成数字
  • _为error信息

进程信息

  • os.Args 命令行参数
  • os.Getenv("PATH") 获得环境变量
  • os.Setenv("AA", "BB")
  • buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput() 快速启动子进程获取输入输出

实战

猜数游戏

  • secretNumber := rand.Intn(maxNum) 系统随机数(),不设置随机数种子,不然默认种子都是一样的
  • rand.Seed(time.Now().UnixNano()) 加上一个随机数种子
  • reader := bufio.NewReader(os.Stdin) 读取用户输入
  • input, err := reader.ReadString('\n') 获取输入字符串
  • input = strings.TrimSuffix(input, "\n") TrimSuffix 取空格
  • _, err := fmt.Scanf("%d", &guess) 一行解决取数字,去除上面所有的烦恼