Go语言上手——基础语言 | 青训营笔记

111 阅读4分钟

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

PartI. 基本语法与变量类型

1.1 Hello World

1
package main // 在go中一个package只有一个main方法
23
import "fmt" // 导入标准库
45
func main(){
6
  fmt.Println("Hello,world!")
7
}

1.2.变量

1
package main
23
import (
4
  "fmt"
5
  "math"
6
)
78
func main(){
9
  var a = "test"
10
  var b, c int = 1, 2
11
  var d = true
12
  var e float64
13
  f := float32(e)
14
  g := a + "world"
15
  fmt.Println(a, b, c, d, e, f, g)
16
  const s string = "constString"
17
  const h = 1000000
18
  fmt.Println(math.Sin(h))
19
}

go语言是一门强类型语言,每个变量都有对应的类型。

常用类型

  • 整数类型:(u)int,(u)int8,(u)int16,(u)int32,(u)int64,uintptr(指针),byte,rune
  • 字符(串)类型:byte,rune,string(string是内置类型,可以直接使用==比较字符串)
  • 浮点型:float32,float64
  • 布尔型:bool

声明变量主要有两种方式:

  • 通过var 变量名 (后置类型)来声明变量,其中后置类型可省略,一般会自动推导变量的类型。
  • 使用变量 := 值的形式来声明变量。这种情况下当前变量必须未声明,否则会报错。

声明常量

go语言可以通过const关键字来声明常量,go中的常量没有确定的类型,会根据使用的上下文来自动确定类型。

Hint

go语言会检查当前变量是否被使用,如果声明后未被使用则会报错。

报错

1.3 if else分支

1
import "fmt"
23
func main() {
4
  if 5%2 == 0 {
5
    fmt.Println("5 is even")
6
  } else {
7
    fmt.Println("5 is odd")
8
  }
910
  if num:=19; num>18 {
11
    fmt.Println("yes")
12
  }else {
13
    fmt.Println("no")
14
  }
15
}
  • go语言中if后面不必写括号
  • 在if语句中可以定义局部变量,此时该变量仅在if语句中生效。

1.4 循环

1
import "fmt"
23
func main() {
4
  i := 1
5
  for i <= 3{
6
    fmt.Println(i)
7
    i++
8
  }
9
  for {
10
    fmt.Println("hello")
11
    break
12
  }
13
  for j := 0; j < 10; j++ {
14
    fmt.Println(j)
15
  }
16
}

在go语言中仅有for循环,不支持while、do while循环。最简单的for循环就是在后面什么都不加,默认为死循环,需要通过break去进行中止。其他的类似于其他语言,唯一的区别就是语句不需要加括号,直接写即可。

1.5 switch

1
import (
2
  "fmt"
3
  "time"
4
)
56
func main() {
7
  i := 1
8
  switch i {
9
  case 1:
10
    fmt.Println("111")
11
  default:
12
    fmt.Println("other")
13
  }
14
  t := time.Now()
15
  switch {
16
  case t.Hour() > 12 :
17
    fmt.Println("过了12点了")
18
  default:
19
    fmt.Println("还没过12点")
20
  }
21
}

在go语言中switch分支功能强大。switch case里面不用加break也能自动的结束。另外,siwtch后面可以不加其他变量,然后再case中写条件分支来判断,比写多个if else会清晰很多。

1.6 数组

1
import "fmt"
23
func main() {
4
  var a [5]int
5
  a[1] = 20
6
  fmt.Println(a[1], len(a))
78
  b := [5]int{1,2,3,4,5} // 数组初始化赋值
9
  fmt.Println(b) // 打印的就是数组内容而非数组地址
1011
  var darr [3][3] int
12
  for i := 0; i < 3; i++{
13
    for j := 0; j < 3; j++{
14
      darr[i][j] = i + j
15
    }
16
  }
17
  fmt.Println(darr)
18
}
1
20 5
2
[1 2 3 4 5]
3
[[0 1 2] [1 2 3] [2 3 4]]

不过在真实业务中我们使用切片较多,不常使用数组。

1.7 切片

1
import "fmt"
23
func main() {
4
  s := make([]string, 5)
5
  s[0] = "a"
6
  s[1] = "b"
7
  s[2] = "c"
8
  s[3] = "d"
9
  s[4] = "e"
10
  fmt.Println(s[3], len(s))
1112
  s = append(s, "f")
13
  s = append(s, "g", "h")// 变长参数
14
  fmt.Println(s, len(s))
1516
  c := make([]string, len(s))
17
  copy(c, s) // target, src
18
  fmt.Println(c)
1920
  fmt.Println(s[2:5])
21
  fmt.Println(s[:5])
22
  fmt.Println(s[2:])
23
  fmt.Println(s[:])
24
}
1
d 5
2
[a b c d e f g h] 8
3
[a b c d e f g h]
4
[c d e]
5
[a b c d e]
6
[c d e f g h]
7
[a b c d e f g h]

slice不同于数组,可以任意更改长度,我们通过make(type, len)来创建一个切片。

slice可以像数组一样去取值,也可以通过append(slice, params…)来追加元素。注意这里的params是可变参数列表,可以追加多个,如果容量不够的话会扩容并返回新的slice。

slice具有类似于Python的切片操作,比如可以通过[start:end]取出指定位置的元素[start, end)。但是这里不同于Python的是不支持负数索引。

1.8 map

1
import "fmt"
23
func main() {
4
  m := make(map[string]int) // key: string value:int
5
  m["a"] = 10
6
  m["b"] = 20
7
  fmt.Println(m)
8
  fmt.Println(len(m))
9
  r, ok := m["a"]
10
  fmt.Println(r, ok)
11
  //r, ok := m["e"]
12
  //fmt.Println(r, ok)
13
  delete(m, "a")
14
  fmt.Println(m)
15
}
1
map[a:10 b:20]
2
2
3
10 true
4
// 0 false
5
map[b:20]

map是实际使用过程中最频繁用到的数据结构。我们可以通过make来创建一个空map,这里的map[string]int中string为key的类型,int为value的类型。我们可以通过类似数组取值的方式赋值map(不同于Java的map.put方法)。另外,在取map对应key值的时候可以获取到其bool元素命名为ok,标识着是否成功取到该元素(true or false)

go语言中的map是完全无序的,遍历时候不会按照字母顺序/插入顺序,而是随机顺序。

1.9 range

1
import "fmt"
23
func main() {
4
  nums := []int{2, 3, 4}
5
  sum := 0
6
  for i, num := range nums{
7
    sum += num
8
    if num == 2 {
9
      fmt.Println("index:", i, "num:", num) //index: 0 num: 2
10
    }
11
  }
12
  fmt.Println(sum) // 9
1314
  m := map[string]string{"a": "A", "b": "B"}
15
  for k, v := range m {
16
    fmt.Println(k, v) // a A b B
17
  }
18
  for k := range m {
19
    fmt.Println(k) // a b
20
  }
2122
}

对于一个slice和map,我们可以通过range进行快速遍历。

range遍历时对于数组可以返回index和对应位置的值(如果不需要索引一般使用_来忽略),对于slice则返回key和value。

PartII. 方法、结构体与常见操作

2.1 方法

1
import "fmt"
23
func add(a int, b int) int {
4
  return a + b
5
}
67
func add2(a, b int) int {
8
  return a + b
9
}
1011
func exists(m map[string]string, k string) (v string, ok bool){
12
  v, ok = m[k]
13
  return v, ok
14
}
1516
func main() {
17
  res := add(1, 2)
18
  fmt.Println(res) // 3
1920
  v, ok := exists(map[string]string{"a":"A"}, "a")
21
  fmt.Println(v, ok) // A true
22
}

go语言中变量类型后置,返回值类型也后置。在go函数中可以有多个返回值

2.2 指针

1
import "fmt"
23
func add2(n int){
4
  n += 2
5
}
67
func add(n *int){
8
  *n += 2
9
}
1011
func main() {
12
  n := 5
13
  add2(n)
14
  fmt.Println(n) // 5
15
  add(&n) // 注意是传地址,因为参数是指针
16
  fmt.Println(n) // 7
17
}

go语言也支持指针,但是相比于C/C++中的指针支持的操作很有限。传指针的主要用途就是对传入参数进行修改。

2.3 结构体

1
import "fmt"
23
type user struct{
4
  name string
5
  password string
6
}
78
func main() {
9
  a := user{name: "zhangsan", password: "123456"}
10
  b := user{"zhangsan", "123456"}
11
  c := user{name:"wang"}
12
  c.password = "1234"
13
  d := user{}
14
  fmt.Println(a, b, c, d) // {zhangsan 123456} {zhangsan 123456} {wang 1234} { }
15
}

在go语言中支持定义结构体,结构体可以通过{}进行直接赋值,也可以通过.获取对应的成员变量进行赋值。

结构体也支持指针,能够实现对结构体内部的修改。

2.4 结构体方法

1
import "fmt"
23
type user struct{
4
  name string
5
  password string
6
}
78
func (u user) checkPassWord(password string) bool {
9
  return u.password == password
10
}
1112
func (u *user) resetPassWOrd(password string) {
13
  u.password = password
14
}
1516
func main() {
17
  a := user{name: "zhangsan", password: "123456"}
18
  fmt.Println(a.checkPassWord("123456")) // true
19
  a.resetPassWOrd("123")
20
  fmt.Println(a) //{zhangsan 123}
21
}

可以通过在方法名前指定类型来定义结构体方法,注意这里传参也可以传其本身或者是指针。传本身实际是传一个拷贝,无法对结构体内部进行修改。

2.5 错误处理

1
import (
2
  "errors"
3
  "fmt"
4
)
56
type user struct{
7
  name string
8
  password string
9
}
1011
func findUser(users []user, name string) (v *user, err error){
12
  for _, u := range users {
13
    if u.name == name {
14
      return &u, nil
15
    }
16
  }
17
  return nil, errors.New("not found")
18
}
1920
func main() {
21
  u, err := findUser([]user{{"zhangsan", "123456"}}, "zhangsan")
22
  if err != nil {
23
    fmt.Println(err)
24
    return
25
  }
26
  fmt.Println(u.name)
2728
  if u, err := findUser([]user{{"zhangsan", "123456"}}, "zs"); err != nil{
29
    fmt.Println(err)
30
    return
31
  }else{
32
    fmt.Println(u.name)
33
  }
34
}

在go语言中一般用一个单独的返回值来传递错误信息。

2.6 字符串操作

1
import (
2
  "fmt"
3
  "strings"
4
)
56
func main() {
7
  a := "hello"
8
  fmt.Println(strings.Contains(a, "ll")) // true
9
  fmt.Println(strings.Count(a, "l")) // 2
10
  fmt.Println(strings.HasPrefix(a, "he")) // true
11
  fmt.Println(strings.Index(a, "ll")) // 2
12
  fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
13
  fmt.Println(strings.Repeat(a, 2)) // hellohello
14
  fmt.Println(strings.Replace(a, "e", "E", - 1)) // hEllo
15
  fmt.Println(strings.Split("a-b-c", "-")) // [a b c]
16
  fmt.Println(len("你好")) // 6
17
}

使用strings工具类的方法即可,类似于Java中工具类,不过Java中String类操作是其成员方法。

2.7 JSON处理

1
import (
2
  "encoding/json"
3
  "fmt"
4
)
56
type userInfo struct{
7
  Name string
8
  Age int
9
  Hobby []string
10
}
1112
func main() {
13
  a := userInfo{"zhangsan", 19, []string{"golang","java"}}
14
  buf, err := json.Marshal(a)
15
  if err != nil {
16
    panic(err)
17
  }
18
  fmt.Println(buf) // [123 34 78 ....]
19
  fmt.Println(string(buf)) // {"Name":"zhangsan","Age":19,"Hobby":["golang","java"]}
2021
  var temp userInfo
22
  err = json.Unmarshal(buf, &temp)
23
  if err != nil{
24
    panic(err)
25
  }
26
  fmt.Printf("%#v\n", temp)
27
}

go语言中的JSON操作比较简单,对于一个已有的结构体,只要其字段首字母是大写(公开字段),那么这个结构体就能够用JSON.marshal进行序列化变成JSON字符串,也可以通过JSON.Unmarshal方法进行反序列化。如果对序列化的风格结果有要求,可以在结构体中使用json tag来改变json序列化的字段名。

2.8 时间处理

1
import (
2
  "fmt"
3
  "time"
4
)
56
func main() {
7
  now := time.Now()
8
  fmt.Println(now) // 2022-05-08 14:16:21.865038 +0800 CST m=+0.000059543
9
  t := time.Date(2022, 5, 8, 13,45,23,0,time.UTC)
10
  fmt.Println(t) // 2022-05-08 13:45:23 +0000 UTC
11
  fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 May 8 13 45
12
  fmt.Println(t.Format("2006-01-02 15:04:05")) // go日期初始化定义的时间,不能更改 2022-05-08 13:45:23
13
  fmt.Println(t.Format("06.01.02 15-04-05")) // 22.05.08 13-45-23
14
  diff := now.Sub(t)
15
  fmt.Println(diff.Hours(), diff.Minutes()) //-7.411009986944444 -444.66059921666664
16
  fmt.Println(now.Unix()) // 1651990921 获取时间戳
17
}

go可通过time的一序列方法获取当前时间,对时间进行格式化,对时间进行作差等。

PartIII. 实战

3.1 猜谜游戏

1
import (
2
  "bufio"
3
  "fmt"
4
  "math/rand"
5
  "os"
6
  "strconv"
7
  "strings"
8
  "time"
9
)
1011
func main(){
12
  maxNum := 100
13
  rand.Seed(time.Now().UnixNano()) // 使用时间戳初始化随机种子
14
  secretNumber := rand.Intn(maxNum)
15
  //fmt.Println(secretNumber)
1617
  fmt.Println("Please input your guess")
18
  reader := bufio.NewReader(os.Stdin)
1920
  for {
21
    input, err := reader.ReadString('\n')
22
    if err != nil {
23
      fmt.Println("An error occurred while reading input. Please try again", err)
24
      continue
25
    }
26
    input = strings.TrimSuffix(input, "\n")
2728
    guess, err := strconv.Atoi(input)
29
    if err != nil {
30
      fmt.Println("Invalid input. Please enter an integer value!")
31
      continue
32
    }
33
    fmt.Println("Your guess is", guess)
34
    if guess > secretNumber {
35
      fmt.Println("Your guess is bigger than the secret number. Please try again")
36
    } else if guess < secretNumber {
37
      fmt.Println("Your guess is smaller than the secret number. Please try again")
38
    } else {
39
      fmt.Println("Correct, you Legend!")
40
      break
41
    }
42
  }
43
}
44

经典实验题emm,主要了解语言特性,这里注意输入是通过bufio.NewReader(os.Stdin)获取输入流。

3.2 在线字典

1
package main
23
import (
4
  "bufio"
5
  "bytes"
6
  "encoding/json"
7
  "fmt"
8
  "io/ioutil"
9
  "log"
10
  "net/http"
11
  "os"
12
  "strings"
13
)
1415
type DictRequest struct {
16
  TransType string `json:"trans_type"`
17
  Source string `json:"source"`
18
  UserID string `json:"user_id"`
19
}
2021
type DictResponse struct {
22
  Rc int `json:"rc"`
23
  Wiki struct {
24
    KnownInLaguages int `json:"known_in_laguages"`
25
    Description struct {
26
      Source string `json:"source"`
27
      Target interface{} `json:"target"`
28
    } `json:"description"`
29
    ID string `json:"id"`
30
    Item struct {
31
      Source string `json:"source"`
32
      Target string `json:"target"`
33
    } `json:"item"`
34
    ImageURL string `json:"image_url"`
35
    IsSubject string `json:"is_subject"`
36
    Sitelink string `json:"sitelink"`
37
  } `json:"wiki"`
38
  Dictionary struct {
39
    Prons struct {
40
      EnUs string `json:"en-us"`
41
      En string `json:"en"`
42
    } `json:"prons"`
43
    Explanations []string `json:"explanations"`
44
    Synonym []string `json:"synonym"`
45
    Antonym []string `json:"antonym"`
46
    WqxExample [][]string `json:"wqx_example"`
47
    Entry string `json:"entry"`
48
    Type string `json:"type"`
49
    Related []interface{} `json:"related"`
50
    Source string `json:"source"`
51
  } `json:"dictionary"`
52
}
5354
func query(word string) {
55
  client := &http.Client{}
56
  //var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
57
  request := DictRequest{"en2zh", word, ""}
58
  buf, err := json.Marshal(request)
59
  if err != nil{
60
    log.Fatal(err)
61
  }
62
  var data = bytes.NewReader(buf)
6364
  req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
65
  if err != nil {
66
    log.Fatal(err)
67
  }
68
  req.Header.Set("Accept", "application/json, text/plain, */*")
69
  req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
70
  req.Header.Set("Connection", "keep-alive")
71
  req.Header.Set("Content-Type", "application/json;charset=UTF-8")
72
  req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
73
  req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
74
  req.Header.Set("Sec-Fetch-Dest", "empty")
75
  req.Header.Set("Sec-Fetch-Mode", "cors")
76
  req.Header.Set("Sec-Fetch-Site", "cross-site")
77
  req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36")
78
  req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
79
  req.Header.Set("app-name", "xy")
80
  req.Header.Set("os-type", "web")
81
  req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"`)
82
  req.Header.Set("sec-ch-ua-mobile", "?0")
83
  req.Header.Set("sec-ch-ua-platform", `"macOS"`)
84
  resp, err := client.Do(req)
85
  if err != nil {
86
    log.Fatal(err)
87
  }
88
  defer resp.Body.Close()
89
  bodyText, err := ioutil.ReadAll(resp.Body)
90
  if err != nil {
91
    log.Fatal(err)
92
  }
93
  if resp.StatusCode != 200 {
94
    log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
95
  }
96
  //fmt.Printf("%s\n", bodyText)
97
  var dictResponse DictResponse
98
  err = json.Unmarshal(bodyText, &dictResponse)
99
  if err != nil{
100
    log.Fatal(err)
101
  }
102
  fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
103
  for _, item := range dictResponse.Dictionary.Explanations {
104
    fmt.Println(item)
105
  }
106
}
107108
func main() {
109
  reader := bufio.NewReader(os.Stdin)
110
  word, err := reader.ReadString('\n')
111
  if err != nil {
112
    fmt.Println(err)
113
    return
114
  }
115
  word = strings.TrimSuffix(word, "\n")
116
  query(word)
117

效果:

在线字典

\