Go语言入门指南

92 阅读1分钟

Go基本语法

基础知识

  1. package`表示该文件属于哪个包

  2. import表示该文件导入哪些包

  3. 变量类型

    1. 使用var定义时可以不声明数据类型,go会自动识别,如var a = "initial"
    2. 使用var时可以显式声明,如var b, c int = 1, 2
    3. 字符串是内置类型,可以直接通过+拼接,如g := a + "foo"
    4. 使用const声明常量,go可以通过上下文自动识别数据类型,如const s string = "123"

流程控制

  1. if-else

    1. if条件判断后不加括号
    2. if后必须加大括号,不能写在同一行
     if 7%2 == 0 {
         fmt.Println("7 is even")
     } else {
         fmt.Println("7 is odd")
     }
     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")
     }
    
  2. for(同if-else,条件判断不需要加括号)

    1. 条件判断什么都不写表示死循环
    2. for后条件中,初始化、条件判断、自增值这三部分每一部分都可以省略,中间用;隔开
     for {       //全部省略
         fmt.Println("loop")
         break
     }
     for j := 7; j < 9; j++ {    //全写
         fmt.Println(j)
     }
     for i <= 3 {                //部分省略
         fmt.Println(i)
         i = i + 1
     }
    
  3. switch(同if-else,条件判断不需要括号)

    1. GoLang中,不需要在每一条分支下加break,执行完对应分支下语句后会自动跳出
    2. switch后也可以不加任何条件判断,在case的各分支下进行判断
     t := time.Now()
     switch {
     case t.Hour() < 12:
         fmt.Println("It's before noon")
     default:
         fmt.Println("It's after noon")
     }
    

数据结构

  1. 数组 (长度固定)

    1. 定义时在数据类型前声明数组大小,如var a [5]int
    2. 定义时自动初始化
    3. 可以直接通过fmt.Print来打印,结果显示为[xxxxx]
     var a [5]int
     a[4] = 100
     fmt.Println("get:", a[2])
     fmt.Println("len:", len(a))
     ​
     b := [5]int{1, 2, 3, 4, 5}
     fmt.Println(b)
     ​
     var twoD [2][3]int
     for i := 0; i < 2; i++ {
         for j := 0; j < 3; j++ {
             twoD[i][j] = i + j
         }
     }
     fmt.Println("2d: ", twoD)
    
  2. slice切片 (可变长数组)

    1. 通过make来定义切片,第一个参数表示数据类型,第二个参数表示初始切片大小,如s := make([]string, 3)
    2. 切片通过append来添加元素,需要注意的是,添加结果必须赋值回去,因为切片的底层本质是存储了长度+容量+指向数组的指针,添加时如果发生扩容会产生一个新的指针,因此需要把新的指针返回回去。如s = s.append(s, "d")
    3. 可以用copy(target, source)来把一个切片赋值给另一个切片
    4. Go中的切片可以实现Python中的切片操作,如fmt.Println(s[2:5])表示打印切片s中下标从2到5 (不包括5) 的元素
     s := make([]string, 3)
     s[0] = "a"
     s[1] = "b"
     s[2] = "c"
     fmt.Println("get:", s[2])   // c
     fmt.Println("len:", len(s)) // 3
     ​
     s = append(s, "d")
     s = append(s, "e", "f")
     fmt.Println(s) // [a b c d e f]
     ​
     c := make([]string, len(s))
     copy(c, s)
     fmt.Println(c) // [a b c d e f]
     fmt.Println(s[2:5]) // [c d e]
    
  3. Map (哈希、字典、键值对)

    1. 通过make(Map[key]value)来建立Map,key表示键的数据类型,value表示值的数据类型,如m := make(Map[string]int)建立一个string-int的键值对
    2. 直接通过赋值来向map中添加元素,如m["one"] = 1向m中添加一个"one":1的键值对
    3. 获取map中的值时,可以设置两个参数,第一个参数表示返回值,第二个参数表示map中是否有该键,如r, ok = map["unknown"],若没有该键,返回值默认为0
    4. 通过delete(map, key)来对Map中的键值对进行删除
     m := make(map[string]int)
     m["one"] = 1
     m["two"] = 2
     fmt.Println(m)           // map[one:1 two:2]
     fmt.Println(len(m))      // 2
     fmt.Println(m["one"])    // 1
     fmt.Println(m["unknow"]) // 0
     ​
     r, ok := m["unknow"]
     fmt.Println(r, ok) // 0 false
     ​
     delete(m, "one")
    

基础语法

  1. range遍历

    1. 可以通过range快速遍历slicemap
    2. 格式如for i, num := range nums,其中当numsslice切片时,i表示索引,num表示对应位置的值;当numsmap键值对时,i表示keynum表示value
     for i, num := range nums {
         sum += num
         if num == 2 {
             fmt.Println("index:", i, "num:", num) // index: 0 num: 2
         }
     }
     fmt.Println(sum) // 9
     ​
     m := map[string]string{"a": "A", "b": "B"}
     for k, v := range m {
         fmt.Println(k, v) // b 8; a A
     }
     for k := range m {
         fmt.Println("key", k) // key a; key b
     }
    
  2. function函数

    1. 参数的变量类型后置,即先声明变量名,再声明变量类型,如func add(a int, b int)
    2. 多数函数返回不止一个值,第一个值为真正的返回结果,第二个值通常为错误信息,一般为bool类型
    3. 不指定返回值啧默认为空 (void)
     func add(a int, b int) int {
         return a + b
     }
     func exists(m map[string]string, k string) (v string, ok bool) {
         v, ok = m[k]
         return v, ok
     }
    
  3. point指针

    1. 主要作用为对传入的参数进行修改
    2. 指针的*放在数据类型前
     func add2ptr(n *int) {
         *n += 2
     }
    
  4. struct结构体

    1. 通过type name struct{}定义结构体
    2. 定义结构体变量时没有指定的字段会默认置空
    3. 通过name.field来获取/修改结构体对应字段的值
     type user struct {
         name     string
         password string
     }
     a := user{name: "wang", password: "1024"}
         b := user{"wang", "1024"}
         c := user{name: "wang"}
         c.password = "1024"
    
  5. struct-method结构体方法

    1. 可以为结构体定义对应的方法,类似其它语言的成员函数
    2. 通过name.func来调用对应的结构体方法
     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
     }
    
  6. error错误处理

    1. 一般函数通过定义第二个返回值error来实现类似Java中的try-catch效果,error可以通过errors.New("xxxx")来定义
    2. 调用目标函数后先判断error是否存在,若不存在才能继续执行相关业务逻辑
     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")
     }
    
  7. string常用字符串操作

    1. strings.Contains(s, "xx"),判断字符串中是否包含xx
    2. strings.Count(s, "xx"),求字符串中xx的出现次数
    3. strings.HasPrefix(s, "xx"),判断字符串是否以xx打头
    4. strings.Index(s, "xx"),查找xx字符串的位置
    5. strings.Join([]string{a, b}, "-"),用-连接多个字符串
    6. strings.Repeat(s, int),将字符串重复数遍后返回
    7. strings.Replace(s, "a", "b", c),用b替换a,重复c
    8. strings.Split(s, "-"),按-分割字符串
    9. strings.ToLower(s),将字符串转换为小写
    10. strings.ToUpper(s),将字符串转换为大写
    11. len(s):返回字符串长度,一个中文汉字可能对应多个字符
  8. fmt 字符串格式化

    1. 通过fmt.Println()打印对应变量并换行
    2. 通过fmt.Printf("%v", s)格式化打印任意类型的变量,类似C中的printf
    3. 通过fmt.Printf("%+v", s)fmt.Printf("%#v", s)来打印变量详细信息
    4. 通过fmt.Printf("%.2f", s)打印保留两位小数的浮点数
     fmt.Printf("p=%v\n", p)  // p={1 2}
     fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
     fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
     ​
     f := 3.141592653
     fmt.Println(f)          // 3.141592653
     fmt.Printf("%.2f\n", f) // 3.14
    
  9. json

    1. 序列化前提:结构体的每个字段名首字母为大写,若不为大写则序列化结果不包含对应字段
    2. 序列化操作:通过buf, err = json.Marshal(s)来对结构体进行序列化,打印buf时需要将其转换为string类型,输出string类型时为键值对的形式
    3. 若要输出常见json格式,需要通过buf, err = json.MarshalIndent(a, "", "\t")实现,结果存放在buf中,a为原始数据
    4. 反序列化:通过err = json.Unmarshal(buf, &b)来对序列化结果buf进行反序列化,将结果存放在b中,返回err
     a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
     buf, err := json.Marshal(a)
     if err != nil {
         panic(err)
     }
     fmt.Println(buf)         // [123 34 78 97...]
     fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}
     ​
     buf, err = json.MarshalIndent(a, "", "\t")
     if err != nil {
         panic(err)
     }
     fmt.Println(string(buf))
     ​
     var b userInfo
     err = json.Unmarshal(buf, &b)
     if err != nil {
         panic(err)
     }
     fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
    
  10. time时间处理

    1. 通过time.Now()获取当前时间
    2. 通过time.Date(Year, Month, Day, Hour, Minute, Second, Offset, time.UTC)来构造一个指定的时间,其中Offset表示距离标准时区的偏移
    3. 可以通过t.Year(), t.Month()....获取对应时间信息
    4. 通过diff = time1.sub(time2)可以获得一个时间差,可以通过diff.Minute(), diff.Second()获取对应时间信息
    5. 通过time1.format("2006-01-02 15:04:05")来格式化一个时间信息,该字符串为固定格式
    6. 通过time.Parse("2006-01-02 15:04:05", s)来将一个字符串格式化为时间信息
    7. 通过time.Unix()获取时间信息对应的时间戳
  11. strconv数字解析

    1. 需要先导入strconv包才能实现字符串解析为数字
    2. 通过strconv.ParseFloat(s, int)来讲指定字符串s解析为指定精度的浮点数
    3. 通过strconv.ParseInt(s, int1, int2)来讲指定字符串s解析为指定进制 int1 指定精度int2的政整形
    4. 通过strconv.Atoi(s),Go将自动识别转换对应字符串
     f, _ := strconv.ParseFloat("1.234", 64)
     fmt.Println(f) // 1.234
     ​
     n, _ := strconv.ParseInt("111", 10, 64)
     fmt.Println(n) // 111
     ​
     n, _ = strconv.ParseInt("0x1000", 0, 64)
     fmt.Println(n) // 4096
     ​
     n2, _ := strconv.Atoi("123")
     fmt.Println(n2) // 123
     ​
     n2, err := strconv.Atoi("AAA")
     fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
    
  12. env进程信息

    1. 先导入"os", "os/exec"
    2. 通过os.Args获取当前进程信息
    3. 通过os.Getenv()os.Setenv()来获取/设置环境变量

实现一个简单的数字炸弹游戏

 package main
 ​
 import (
     "bufio"
     "fmt"
     "math/rand"
     "os"
     "strconv"
     "strings"
     "time"
 )
 ​
 func main() {
     maxNum := 100
     rand.Seed(time.Now().UnixNano())  //根据时间戳初始化随机种子
     secretNumber := rand.Intn(maxNum) //根据随机种子生成随机数
 ​
     fmt.Println("Please input your guess") //提示用户输入
     reader := bufio.NewReader(os.Stdin)    //重定义输入流
     for {
         input, err := reader.ReadString('\n') //读取用户输入
         if err != nil {
             fmt.Println("An error occured while reading input. Please try again", err)
             continue
         }
         input = strings.Trim(input, "\r\n") //将最后输入的回车去除
 ​
         guess, err := strconv.Atoi(input) //将输入转换为数字
         if err != nil {
             fmt.Println("Invalid input. Please enter an integer value")
             continue
         }
         fmt.Println("You guess is", guess) //输出用户的输入
         if guess > secretNumber {          //进行判断
             fmt.Println("Your guess is bigger than the secret number. Please try again")
         } else if guess < secretNumber {
             fmt.Println("Your guess is smaller than the secret number. Please try again")
         } else {
             fmt.Println("Correct, you Legend!")
             break
         }
     }
 }