这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
什么是Go语言
- 高性能、高并发
- 语法简单、学习曲线平缓
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
安装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语句
- 可省略条件表达式括号。
- 持初始化语句,可定义代码块局部变量。
- 代码块左 括号必须在条件表达式尾部。
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的文档了解更为深刻,希望自己能继续保持这种学习状态,加油!
引用
该文章部分内容来自于以下课程或网页: