Go 语言基础 | 青训营笔记

63 阅读11分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

什么是Go语言

  1. 高性能、高并发
  2. 语法简单、学习曲线平缓
  3. 丰富的标准库
  4. 完善的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收

安装Go语言和配置环境,并且使用编辑器

从Go官网下载地址:golang.org/dl/下载完后,由于自己已经下载好vscode我选择的时直接安装插件来编辑Go语言程序,启动vscode选择插件->搜go选择Go for Visual Studio Code插件点击安装即可。详细教程我参考的是B站的# VSCode下快速配置Go语言开发环境

一、本堂课重点内容

go语言的基础语法

二、详细知识点介绍():

代码都是跟着课程打一遍的

2.1 基础语法-Hello World

package main
import "fmt"
func main() {
    fmt.Println("Hello world!")
}

2.2 基础语法-变量

Go语言的变量声明格式为:

var 变量名 变量类型(标准声明) var (变量名1 变量类型,变量名2 变量类型....)(批量声明)

变量初始化:

var 变量名 类型 = 表达式(全局变量初始化)变量名 := 表达式(局部变量初始化)

func main() {
    var a = "inital"
    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) //        inital 1 2 true 0 0
    fmt.Println(g)                //     initalfoo
}

2.3基础语法-if else

go的if语句

  1. 可省略条件表达式括号。
  2. 持初始化语句,可定义代码块局部变量。
  3. 代码块左 括号必须在条件表达式尾部。

if 布尔表达式 { /* 在布尔表达式为 true 时执行 */ }

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 divosoble 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")
    }
}

2.4 基础语法-循环

Go语言的For循环有3中形式,只有其中的一种使用分号。

for init; condition; post { }

for condition { }

for { }

func main() {
    i := 1
    for {
        fmt.Println("loop")
        break
    }
    for j := 7; j < 9; j++ {
        fmt.Println(j)
    }
    for i <= 3 {
        fmt.Println(i)
        i = i + 1
    }
}

2.5 基础语法-switch

switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上直下逐一测试,直到匹配为止。 Golang switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。Go中switch更加强大可以使用任何变量,甚至可以取代任意的if-else语句。

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")
    }
}

2.6 基础语法-数组

Golang Array和以往认知的数组有很大不同:1.数组:是同一种数据类型的固定长度的序列。2.数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。3.长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型。4.数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1 for i := 0; i < len(a); i++ { } for index, v := range a { }5.访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic6.数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。 7.支持 "=="、"!=" 操作符,因为内存总是被初始化过的。 8.指针数组 [n]*T,数组指针 *[n]T

func main() {
    var a [5]int //定义数组a变量
    a[4] = 100
    fmt.Println(a[4], len(a))
    b := [5]int{1, 2, 3, 4, 5} //直接定义初始数组b
    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.7 基础语法-切片

通过make来创建切片

变量名 := make([]类型名 长度)

变量名 := make([]类型名 长度 容量)

append(类型名,追加元素)内置函数操作切片--追加

func main() {
    s := make([]string, 3)
    s[0] = "a"
    s[1] = "b"
    s[2] = "c"
    fmt.Println("get:", s[2])
    fmt.Println("len:", len(s))
    s = append(s, "d")
    s = append(s, "e", "f")
    fmt.Println(s)
    c := make([]string, len(s))
    copy(c, s)
    fmt.Println(c)
    good := []string{"g", "o", "o", "d"}
    fmt.Println(good)
}

切片操作与python的操作类似

    fmt.Println(s[2:5])
    fmt.Println(s[:5])
    fmt.Println(s[2:])

2.8 基础语法-map

在其它语言中可能叫做哈希或者字典,使用最频繁的数据结构Go语言中map的定义语法如下:

map[keyType]ValueType

func main() {
    m := make(map[string]int) // map一个类型为string第二个类型为int,map为无序的输出随机
    m["one"] = 1
    m["two"] = 2
    fmt.Println(m)
    fmt.Println(len(m))
    fmt.Println(m["one"])
    fmt.Println(m["unknow"])
    r, ok := m["unknow"]
    fmt.Println(r, ok)
    delete(m, "one")
    m2 := map[string]int{"one": 1, "two": 2}
    var m3 = map[string]int{"one": 1, "two": 2}
    fmt.Println(m2, m3)
}

2.9 基础语法-range

Golang range类似迭代器操作,返回 (索引, 值) 或 (键, 值)。

for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。

可忽略不想要的返回值,或 "_"`这个特殊变量。

func main() {
    nums := []int{2, 3, 4}
    sum := 0
    for i, num := range nums {
        sum += sum
        if num == 2 {
            fmt.Println("index:", i, "num:", num)
        }
    }
    fmt.Println(sum)
    m := map[string]string{"a": "A", "b": "B"}
    for k, v := range m {
        fmt.Println(k, v)
    }
    for k := range m {
        fmt.Println("key", k)
    }
}

2.10 基础语法-函数

函数声明包含一个函数名,参数列表, 返回值列表和函数体。如果函数没有返回值,则返回列表可以省略。函数从第一条语句开始执行,直到执行return语句或者执行函数的最后一条语句。并函函数可以没有参数或接受多个参数。注意类型在变量名之后 。当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。函数可以返回任意数量的返回值。使用关键字 func 定义函数,左大括号依旧不能另起一行。

func add(a int, b int) int {    //分开定义变量类型的两数相加函数
    return a + b
}
​
func add2(a, b int) int {       //一起定义变量类型的两数相加函数
    return a + b
}

Golang中函数返回值都是返回多个值,在源生中第一个值是真正的返回结果,第二个值是错误信息

func exists(m map[string]string, k string) (v string, ok bool) {
    v, ok = m[k]
    return v, ok
}

2.11 基础语法-指针

Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。类型指针不能进行偏移和运算。Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)。这点和c语言很相似

func add2(n int) {
    n += 2
}
​
func add2ptr(n *int) {
    *n += 2
}
​
func main() {
    n := 5
    add2(n)
    fmt.Println(n)//5
    add2ptr(&n)
    fmt.Println(n)//7
}

2.12 基础语法-结构体

在Go语言中有一些基本的数据类型,如string、整型、浮点型、布尔等数据类型,Go语言中可以使用type关键字来定义自定义类型。自定义类型是定义了一个全新的类型。我们可以基于内置的基本类型定义,也可以通过struct定义。这点与C语言也很类似只不过定义及结构体名在struct之前,字段名在字段类型之前。

type user struct {
    name     string
    password string
}

2.13 基础语法-结构体方法

Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者 self。

方法的定义格式如下:

func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) { 函数体 }

其中,1.接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名的第一个小写字母,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。 2.接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。 3.方法名、参数列表、返回参数:具体格式与函数定义相同。

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"))
}

2.14 基础语法-错误处理

Go 语言中鼓励人们在任何 创建方法之后去检查错误。不同于Java中的异常,Go中能够很清晰的知道哪个函数出现了错误并且只使用简单的if-else处理错误。函数体里加一个error返回类型代表只能返回错误。没有error时取返回值可能发生空指针错误。字符串变量的默认值为空字符串。 布尔型变量默认为false。 切片、函数、指针变量的默认为nil(类似为NULL)

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")
}

2.15 基础语法-字符串操作

字符串的常用操作方法:

len(str) 求长度 +或fmt.S

printf 拼接字符串 strings.

Split 分割 strings.

Contains 判断是否包含 strings.

Count 判读元素出现个数

HasPrefix,HasSuffix 前缀/后缀判断 strings.

Index(),LastIndex() 子串出现的位置 strings.

Repeat重复strings.

Replace替换字符串中的元素

Join(a[]string, sep string) join操作

ToLower/ToUpper大小写转化

注意:汉字字符串的长度不等于个数

package main
​
import (
    "fmt"
    "strings"
)
​
func main() {
    a := "hello"
    fmt.Println(strings.Contains(a, "ll"))                  //true
    fmt.Println(strings.Count(a, "l"))                      //2
    fmt.Println(strings.HasPrefix(a, "he"))                 //true
    fmt.Println(strings.HasSuffix(a, "llo"))                //true
    fmt.Println(strings.Index(a, "ll")                      //2
    fmt.Println(strings.Join([]string{"he", "llo"}, "-"))   //he-llo
    fmt.Println(strings.Repeat(a, 2))                       //hellohello
    fmt.Println(strings.Replace(a, "e", "E", -1))           //hEllo
    fmt.Println(strings.Split("a-b-c", "-"))                //[a b c]
    fmt.Println(strings.ToLower(a))                         //hello
    fmt.Println(strings.ToUpper(a))                         //HELLO
    fmt.Println(len(a))                                     //5
    b := "你好"
    fmt.Println(len(b))                                     //6
}

2.16 基础语法-字符串格式化

fmt.Printf()类似c语言中printf数字用%d字符用%s。

不同的时:Go中可以用%v打印任何字符类型;%+v可以打印字段名+值;%#v可以打印结构体类型名称和字段名和值。

package main
​
import (
    "fmt"
)
​
type point struct {
    x, y int
}
​
func main() {
    s := "hello"
    n := 123
    p := point{1, 2}
    fmt.Println(s, n) //hello 123
    fmt.Println(p)    //{1 2}
​
    fmt.Printf("s=%v\n", s)  //s=hello
    fmt.Printf("n=%v\n", n)  //n=123
    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)
    fmt.Printf("%.2f\n", f)
}

2.17 基础语法-JSON处理

每个结构体中的字段名首字母大写就能用json.Marshal()序列化json.Marshal()函数可以对一组数据进行JSON格式的编码,序列化后需要加上字符串类型buf数组否则打印十六进制编码。json使用json.Unmarshal()反序列化到空变量里

package main
​
import (
    "encoding/json"
    "fmt"
)
type userInfo struct {
    Name  string
    Age   int `json:"age"`  //把输出字段的Age改为age
    Hobby []string
}

2.18 基础语法-时间处理

常用事件处理:

time.Minute()分钟;time.Second()秒;time.Year()年;time.Month()月;time.Day()天;time.Hour()小时。

time.Now()获取当前时间

time.Format("2006-01-02 15:04:05")---注意时间格式化特定的time包中的"2006-01-02 15:04:05"。

t1.Sub(t2)一个时间段

time.Unix()获取时间戳

package main
​
import (
    "fmt"
    "time"
)
func main() {
    now := time.Now()
    fmt.Println(now)
    t := time.Date(2023, 1, 15, 1, 25, 36, 0, time.UTC)
    t2 := time.Date(2023, 1, 15, 2, 30, 36, 0, time.UTC)
    fmt.Println(t)
    fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute())
    fmt.Println(t.Format("2006-01-02 15:04:05"))
    diff := t2.Sub(t)
    fmt.Println(diff)
    fmt.Println(diff.Minutes(), diff.Seconds())
    t3, err := time.Parse("2006-01-02 15:04:05", "2022-01-15 01:25:36")
    if err != nil {
        panic(err)
    }
    fmt.Println(t3 == t)
    fmt.Println(now.Unix())
}

2.19 基础语法-数字解析

strconv包实现了基本数据类型与其字符串表示的转换,主要有以下常用函数: Atoi()、Itia()、parse系列、format系列、append系列。

Parse类函数用于转换字符串为给定类型的值:ParseBool()、ParseFloat()、ParseInt()、ParseUint()。

其中ParseInt(数, 进制--0时自动推测, 位数)

Atoi()函数用于将字符串类型的整数转换为int类型

package main
​
import (
    "fmt"
    "strconv"
)
​
func main() {
    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)
}

2,20 基础语法-进程信息

os.Args()获取命令行执行的参数

os.Getenv()获取环境变量

os.Setenv()写入环境变量

exec.Command()快速获子进程和输入输出

package main
​
import (
    "fmt"
    "os"
    "os/exec"
)func main() {
    fmt.Println(os.Args)
    fmt.Println(os.Getenv("PATH"))
    fmt.Println(os.Setenv("AA", "BB"))
​
    buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
    if err != nil {
        panic(err)
    }
    fmt.Println(string(buf))
}

三、项目实践

在最后学习了猜谜游戏案例额来深刻了解Go的语言基础语法的应用,在线字典案例通过抓包来解析请求的发送和响应,SOCKS5代理的实战案例学习了代理配置。

四、课后个人总结

对于我这种后端几乎零基础来说,学习量很大,在配置和了解Go语言基础时不太清楚,于是我配合Go的文档了解更为深刻,希望自己能继续保持这种学习状态,加油!

引用

该文章部分内容来自于以下课程或网页:

Go基础 · Go语言中文文档字节内部课-后端入门 - Go 语言基础与实战案例