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

66 阅读6分钟

萌新,比较水,佬们勿喷。

1.什么是 GO 语言 1.1 GO 语言基本特征 1.2 GO 语言应用优势与不足

2.GO 语言入门 2.1 开发环境 - 安装 Golang & 云上开发 2.2 GO 语言的基础语法

以下是2.2 GO 语言的基础语法的笔记

// 01-hello
package main

import(
	"fmt"
)

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

注意下基本写法 package main 主函数能运行得起得,都是 main.go import 导入内置得包,大多情况导入 'fmt' fmt.Println 标准输出函数

package main

import(
	"fmt"
	"math"
)

func main(){
	var a = "initial"
	
	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)
	fmt.Println(g)
	
	const s string = "constant"
	const h = 50000000
	const i = 3e20 / h
	fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}

" := " 这样只适合于在函数内部这么写 Go 中字符串拼接和 Python 一样用 "+" 定义变量类型时和其他语言不同的是, Go中的变量类型是后置的 如 int 放在后面

package main

import "fmt"

func main(){
	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
	}
}

Go 语言的循环只有一个 for 循环,用法非常灵活 for 循环的 3个选项 都是可有可无的,不想 C 必须有某些选项存在 以上3个 for 循环分别展示了 无任何条件 满条件 和有一个条件 下的循环结构

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

if 条件判断 和 switch 的效果差不多,二者在一定条件下可以相互转换

package main

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

GO 的 case里无须像C语言那样,需要 break 来退出 t := time.Now() 可以用来记录当前的日期

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

数组特点 长度固定,有序 这点不利于其广泛使用 更多时候用的是切片 记住如何创建一个指定大小的空数组 如何创建一个确定的数组

package main  
  
import "fmt"  
  
func main() {  
  
	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]  
	fmt.Println(s[:5]) // [a b c d e]  
	fmt.Println(s[2:]) // [c d e f]  
	  
	good := []string{"g", "o", "o", "d"}  
	fmt.Println(good) // [g o o d]  
}

slice切片特点 是一个可变长度的数组,可以任意时刻去更改长度,操作丰富 make([]string, 3) 代表一个 大小为3的字符类型的切片 append 可以追加元素 append(s, "d") s[2:5] 和 Python 一样包括第一个但不包括第二个

思考 数组的括号可以空着? 不行 因为数组创建之初就是固定的 3
切片的括号可以空着? 可以 切片长度是可变的 3
map [] ->> key 后面的->> value 3

package main

import "fmt"

func main() {
	m := make(map[string]int)
	m["one"] = 1
	m["two"] = 2
	fmt.Println(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)
}

map 也叫 哈希或者字典 里面有很多 key-value对 GO 里的 map 是完全无序的,遍历的时候不会按字母顺序也不会按插入顺序来输出,而是一个偏随机的顺序

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

range 遍历的时候对于数组会返回两个值,第一个是索引,第二个是对应位置的值,假如不想要索引的话,就用下划线去赋位。 遍历 map 的话,第一个值是key,第二个值是value

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  
}

go 中的函数是原生支持返回多个值,在业务代码中几乎所有的函数都返回多个值 第一个值是返回真正的结果,第二个返回错误信息

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  
}

操作有限 思考:拷贝->>指针 add2 只是浅拷贝,只复制值 指针 指向的是地址,地址和本身的值都改了

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  
}

用点来读取和写入结构体内的内容 参数有 指针和非指针两种用法 指针参数:实现对结构体的修改,避免对大结构体的开销

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  
}

理解什么是类成员函数? 如何从普通函数变为类成员函数? 参数还是两种,有指针的可以实现对结构体的修改

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")  
}  
  
func main() {  
	u, err := findUser([]user{{"wang", "1024"}}, "wang")  
	if err != nil {  
		fmt.Println(err)  
		return  
	}  
	fmt.Println(u.name) // wang  
	  
	if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {  
		fmt.Println(err) // not found  
		return  
	} else {  
	fmt.Println(u.name)  
	}  
}

理解如何进行错误处理的

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  
}

字符串,一个中文可能对应了多个字符

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  
}

字符串格式化,细节看代码

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

json操作,非常简单,对于一个结构体,只要保证结构体内第一个字母是大写,就可以用 json.Marshal() 来序列化,变为一个buf数组,简单理解为字符串,当然这需强制类型转换,否则打印出来为16进制字符 `Age int json:"age" Python和Rust中叫装饰器,Java中叫注释

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  
}

时间获取,时间截取,格式化时间字符串,时间字符串解析位时间,时间戳

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) // 0 strconv.Atoi: parsing "AAA": invalid syntax  
}

数字解析 strconv
精准转化,快速转化

package main  
  
import (  
	"fmt"  
	"os"  
	"os/exec"  
)  
  
func main() {  
	// go run example/20-env/main.go a b c d  
	fmt.Println(os.Args) // [/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  
}

进程信息 直接执行源文件 命令行参数 换取或写入环境变量 快速启动子进程并且获取输入输出