实践文章-Go 语言入门指南:基础语法和常用特性解析 | 青训营

65 阅读7分钟

GO学习

[TOC]

1-Go语言基础语法

安装GO

安装VS Code 插件:Go

1.1 介绍

1.1.1什么是GO

特点:

  1. 高性能、高并发

  2. 语法简单、学习曲线平缓

    语法与C++类似但更简洁,无括号,循环只有for

    一周即可学习到开发

  3. 丰富的标准库

    不需要借助第三方库

  4. 完善的工具链

    编译、代码格式化、包管理、完整单元测试框架。。

  5. 静态链接

    部署方便快捷

  6. 快速编译

  7. 跨平台

    路由器、树莓派

  8. 垃圾回收

    无需考虑内存分配释放

1.1.2 使用GO的公司

字节、腾讯、美团、滴滴、百度、Google。。。

1.1.3 字节why go

C++不适合在线Web、最初python

1.2 配置环境

1.3 基础语法

1.3.1 fmt.Println
package main	//是main包的一部分,程序的入口包/入口文件

import (
	"fmt"	//导入包,fmt包用以屏幕输入输出字符串、格式化
)

func main(){
  fmt.Println("hello world!")
}

运行:

go run example/01-hello/main.go——输出hello world

go build example/01-hello/main.go 编译为二进制,后./main运行

访问不了github的解决办法 或挂梯子

文档:[fmt.Println文档](fmt package - fmt - Go Packages)

1.3.2 变量

字符串、整数、浮点型、布尔型

符号的使用和优先级与C、C++类似

变量的声明

​ 法一:var name = value 自动推出变量类型;也可以显式给出var b,c int = 1,2

​ 法二:name := value

常量:把var 改成const ,常量没有固定类型,根据上下文自动确定类型

示例:

package main
import (
	"fmt"
	"math"
)
func main() {
	var a = "initial"
	var b, c int = 1, 2
	var d = true
	var e float64	// e=0 
  
	f := float32(e)	//f=0
	g := a + "foo"
  
	fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0
	fmt.Println(g)                // initialfoo

	const s string = "constant"
	const h = 500000000
	const i = 3e20 / h
  
	fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}
1.3.3 if-else

与C、C++类似,但条件判断中没有括号,条件判断后面必须直接加花括号

  • 注意if-else if -else的写法:}和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")
	}
}
/*输出
	7 is odd
	8 is divisible by 4
	9 has 1 digit
*/
1.3.4 循环for

没有while、do-while循环

只有for循环,可以使用continue继续循环,break跳出循环

package main
import "fmt"
func main() {
	//没有while 没有do-while
	i := 1
	for {	//什么都不写,死循环
		fmt.Println("loop")
		break	//跳出循环
	}
	for j := 7; j < 9; j++ {
		fmt.Println(j)
	}

	for n := 0; n < 5; n++ {
		if n%2 == 0 {
			continue	//继续循环
		}
		fmt.Println(n)
	}
	for i <= 3 {
		fmt.Println(i)
		i = i + 1
	}
}
/*输出
	loop
	7
	8
	1
	3
	1
	2
	3

*/
1.3.5 switch分支结构

switch 后的变量名不需要括号

区别:

​ C++中的case不写break会继续运行完所有的分支;go中默认不需要加break

​ go中更强大,变量可以是任意的,可以是 字符串、结构体等;

​ 也可以取代任意的if-else语句:switch中不加变量,直接在case中写判断语句

package main
import (
	"fmt"
	"time"
)
func main() {
	a := 2
	switch a {	//不加括号
	case 1:
		fmt.Println("one")	//不加break
	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()
	//代替if-else语句
	switch {
	case t.Hour() < 12:
		fmt.Println("It's before noon")
	default:
		fmt.Println("It's after noon")
	}
}
/*输出
	two
	It's after noon
	
*/
1.3.6 数组

利用索引进行读取、写入

不常用数组——固定长度,更常用切片

package main
import "fmt"
func main() {
	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)
}
/*输出
get: 0
len: 5
[1 2 3 4 5]
2d:  [[0 1 2] [1 2 3]]

*/
1.3.7 切片slice

可变长度的数组;任意时刻可以更改长度

创建:make

读取/写入:与数组相同

追加:append,必须将结果赋值给原数组

复制:copy(c,s)

切片:s[2:5]、s[2:]、s[:5]

package main
import "fmt"
func main() {
	s := make([]string, 3) //make创建空
	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") //append追加
	s = append(s, "e", "f")
	fmt.Println(s) // [a b c d e f]

	c := make([]string, len(s))
	copy(c, s)     //copy复制
	fmt.Println(c) // [a b c d e f]

	fmt.Println(s[2:5]) // 左闭右开 [c d e]
	fmt.Println(s[:5])  // [a b c d e]
	fmt.Println(s[2:])  // [c d e f]

	good := []string{"g", "o", "o", "d"}	//不用make创建切片
	fmt.Println(good) // [g o o d]
}
1.3.8 map

其他语言叫做哈希/字典

实际中使用最频繁的数据结构

go中map完全无序,遍历时不按字母顺序也不用插入顺序输出,而是随机

创建:make(map[key的类型]value的类型),创建空的map

删除:delete(map变量名,key的名称)

读取:有两个返回值可接第二个变量ok ——看map中到底是否有这个值存在

package main
import "fmt"
func main() {
	m := make(map[string]int) //创建空的map
	m["one"] = 1              //赋值map
	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"] //这里的r是必须的,否则ok值为0不为false
	fmt.Println(r, ok)   // 0 false

	delete(m, "one") //删除

	m2 := map[string]int{"one": 1, "two": 2}	//创建并初始化
	var m3 = map[string]int{"one": 1, "two": 2}
	fmt.Println(m, m2, m3)
}
1.3.9 range

对于slice和map可以用range快速遍历

对于数组:返回两个值:索引对应位置的值 ;不需要索引的话可以用下划线去忽略

对于map:第一个值是key,第二个值是value

package main
import "fmt"
func main() {
	nums := []int{2, 3, 4}
	sum := 0
	for i, num := range nums {
		sum += num
		if num == 2 {
			fmt.Println("index:", i, "num:", num) // index: 0 num: 2
		}
	}
	fmt.Println(sum) // 9
	//索引用下划线忽略
	for _, num := range nums {
		fmt.Println("num:", num) // num: 2  num: 3  num: 4
	}

	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
	}
}
1.3.10 函数

变量类型后置

多个返回值:第一个-返回结构;第二个-错误信息

package main
import "fmt"
func add(a int, b int) int {	//变量类型后置
	return a + b
}
func add2(a, 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	//返回两个值
}
func main() {
	res := add(1, 2)
	fmt.Println(res) // 3

	v, ok := exists(map[string]string{"a": "A"}, "a")
	fmt.Println(v, ok) // A True
}
1.3.11 指针

相对于C、C++,用法有效

用途:对传入的参数进行修改

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)	//需要加取址符
	fmt.Println(n) // 7
}
1.3.12 结构体

带类型的字段的集合

初始化:使用结构体名称,需要传入初始值,可以只初始化一部分

读取/写入:结构体名称.字段 的方式

作为参数:可以用指针,调用时仍是.字段名的方式

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
}
1.3.13 结构体方法

普通函数->类成员函数

实现方法:带指针,不带指针

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
}
1.3.14 错误处理

使用单独的返回值 来传递错误信息:类型为error

能清晰的知道哪个函数传递了错误

可以使用简单的if-else去处理错误

调用时:接收需要写两个变量

需要引入error包

package main
import (
	"errors"	//需要引入error包
	"fmt"
)

type user struct {
	name     string
	password string
}

func findUser(users []user, name string) (v *user, err error) {	//error类型返回值
	for _, u := range users {
		if u.name == name {
			return &u, nil	//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-else对错误进行处理
	if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
		fmt.Println(err) // not found
		return
	} else {
		fmt.Println(u.name)
	}
}
1.3.15 字符串操作

标准库strings包

Contains()一个字符串是否包含另一个字符串

Count()字符串计数

Index()查找某个字符串位置

Join()连接多个字符串

Repeat()重复

len内置函数,获取字符串长度

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
}
1.3.16 字符串格式化

fmt.Println 打印多个变量并且换行

fmt.Printf %v,打印任意类型的变量,

​ %+v,打印更详细的结构,

​ %#v,更详细

​ %.2f,保留两位小数的浮点数

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)          // 3.141592653
	fmt.Printf("%.2f\n", f) // 3.14
}
1.3.17 JSON操作

保证结构体每个字段的第一个字母是大写

json.Marshal:将 Go 语言中的数据结构转换为 JSON 格式的字节切片

json.MarshalIndent:

json.Unmarshal:将JSON数据解析为Go数据结构的函数。它接受一个包含JSON数据的字节切片和一个指向Go数据结构的指针作为参数,并尝试将JSON数据解析为相应的Go数据结构。如果解析成功,解析后的数据将存储在指针所指向的变量中。

package main
import (
	"encoding/json"
	"fmt"
)
type userInfo struct {
	Name  string
	Age   int `json:"age"`
	Hobby []string
}
func main() {
	a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
	buf, err := json.Marshal(a) //将 Go 语言中的数据结构转换为 JSON 格式的字节切片
	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"}}
}

/*输出
[123 34 78 97 109 101 34 58 34 119 97 110 103 34 44 34 97 103 101 34 58 49 56 44 34 72 111 98 98 121 34 58 91 34 71 111 108 97 110 103 34 44 34 84 121 112 101 83 99 114 105 112 116 34 93 125]
{"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}
{
	"Name": "wang",
	"age": 18,
	"Hobby": [
		"Golang",
		"TypeScript"
	]
}
main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
*/

1.3.18 时间处理

time包

time.Now()

time.Date()

t.Year() t.Day() t.Hour() t.Minute t.Format()

t2.Sub(t)对两个时间做减法,得到一个时间段

package main
import (
	"fmt"
	"time"
)
func main() {
	now := time.Now()
	fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933
	t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
	t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
	fmt.Println(t)                                                  // 2022-03-27 01:25:36 +0000 UTC
	fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
	fmt.Println(t.Format("2006-01-02 15:04:05"))                    //使用‘特定’的时间进行格式化                  // 2022-03-27 01:25:36
	diff := t2.Sub(t)                                               //对两个时间做减法,得到一个时间段
	fmt.Println(diff)                                               // 1h5m0s
	fmt.Println(diff.Minutes(), diff.Seconds())                     // 65 3900 这个时间段转换成分钟、秒
	t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
	if err != nil {
		panic(err)
	}
	fmt.Println(t3 == t)    // true
	fmt.Println(now.Unix()) // 获取时间戳:1648738080
}
1.3.19 数字解析

字符串和数字之间的转换

strconv包

strconv.ParseFloat

strconv.ParseInt

strconv.Atoi

strconvItoa

package main

import (
	"fmt"
	"strconv"
)

func main() {
	f, _ := strconv.ParseFloat("1.234", 64)
	fmt.Println(f) // 1.234

	n, _ := strconv.ParseInt("111", 10, 64) //第一个参数是字符串;第二个参数表示‘进制’;第三个参数64表示返回的是64位精度的整数
	fmt.Println(n)                          // 111

	n, _ = strconv.ParseInt("0x1000", 0, 64) //0的话表示自动推断进制
	fmt.Println(n)                           // 4096

	n2, _ := strconv.Atoi("123") //快速将十进制字符串转换成数字;Itoa:数字转为字符串
	fmt.Println(n2)              // 123

	n3 := strconv.Itoa(666) //Itoa:数字转为字符串 只有一个返回值
	fmt.Println(n3)         // 666

	n2, err := strconv.Atoi("AAA") //不合法的参数
	fmt.Println(n2, err)           // 0 strconv.Atoi: parsing "AAA": invalid syntax
}
1.3.20 进程信息
package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	// go run example/20-env/main.go a b c d 直接执行go的源文件
	fmt.Println(os.Args)           // 长度是5,第一个成员表示二进制自身的路径;[/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
	fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin... 获得环境变量
	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)) // 127.0.0.1       localhost
}