NO.6【实践】Go 语言基础语法和常用特性解析实战笔记 | 青训营

78 阅读1分钟

本文主要记录通过编写运行测试示例项目代码完成go语言基础语法和常见特性的学习实践过程,示例代码存储于github仓库。如有错漏,感谢指正!

一、基础语法

1. 输出

fmt.Println("hello world")

注意:使用fmt输出前 import ("fmt")

2. 变量声明

var a = "initial"             
var b, c int = 1, 2           
var d = true                  
var e float64                 
f := float32(e)               
g := a + "foo"

const s string = "constant"
const h = 500000000
const i = 3e20 / h

注意:go语言中变量声明通常先写变量名称后写变量类型

3. for

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

注意:go语言中循环只有for一种,没有while等语法,其中例子2、3类比于常见for语句,例子4类比于while语句,break、continue用法与其它语言原理相同。

4. if

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

注意:go语言中if条件没有括号,如例子中第二个if。另外一个特别用法是先执行语句后判断,如第一个if,先执行 num:=9 语句,后判断num<0.

5. switch

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语言中switch不需要break语句即可达到条件判断效果。且变量可为数字、字符串、函数等类型。

6. array

  var a [5]int
	b := [5]int{1, 2, 3, 4, 5}
	var twoD [2][3]int
	fmt.Println(twoD)

注意:以数组名字输出时,格式如下:[1 2 3 4 5]、[[0 1 2] [1 2 3]]

7.slice

	s := make([]string, 3)
	s[2] = "c"
	s = append(s, "d")
	fmt.Println(s) // [a b c d]

	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用法与数组类似,区别是可自动扩大长度,封装好了append、copy等函数。

8. 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")

	m2 := map[string]int{"one": 1, "two": 2}
	var m3 = map[string]int{"one": 1, "two": 2}
	fmt.Println(m2, m3)

注意:map结构类key-value形式,可通过key查找对应值。输出map名字格式为:map[one:1 two:2]

9.range

	m := map[string]string{"a": "A", "b": "B"}
	for k, v := range m {
		fmt.Println(k, v) // b 8; a A
	}

注意:range常与循环搭配使用,简化循环语句,例子中k为key,v为value,代替map中的每一个key-value对。

10.func

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
}

注意:go语言中函数形式参数先名称后类型,返回值不止一个,可指定返回值名字直接用于函数体中。

11.point

func add2(n int) {
	n += 2
}

func add2ptr(n *int) {
	*n += 2
}

注意:pointer用法与其它语言类似,传指针类型数据可直接修改原值,传值则不能。

12.struct

type user struct {
	name     string
	password string
}

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"

注意:结构体struct在go语言中最为常见,在数据库存储等领域多有使用,对于函数体的声明修改如上所示。

13.struct-method

func (u user) checkPassword(password string) bool {
	return u.password == password
}

func (u *user) resetPassword(password string) {
	u.password = password
}

注意:在go语言中,结构体可以定义其函数,直接用.操作符进行调用即可,如:a.resetPassword("2048")

14. error

u, err := findUser([]user{{"wang", "1024"}}, "wang")
	if err != nil {
		fmt.Println(err)
	}

注意:使用error前应先import("error"),且当捕捉到错误时,err为自带检测的错误类型,无错误时则err=nil

15. string

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

注意:string类型封装好了contains、count、index等函数直接调用,对照上述代码以及其输出理解其用法

16.fmt

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

注意:除了上述对整型、字符串、point类型变量的输出,还有array、slice、map、结构体等复杂类型的输出形式,参见上述笔记。

17.json

type userInfo struct {
	Name  string
	Age   int `json:"age"`
	Hobby []string
}

buf, err := json.Marshal(a)
buf, err = json.MarshalIndent(a, "", "\t")
err = json.Unmarshal(buf, &b)

注意:结构体变量后面json:"age"用于转化json时的命名替代,使用json前import ("encoding/json"),使用上面三个函数buf的结果为:

1.[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]【(string)buf则输出{"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}

{
	"Name": "wang",
	"age": 18,
	"Hobby": [
		"Golang",
		"TypeScript"
	]
}

3.main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}

18. time

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") // 解析时间字符串为Time类型,因为Go语言的发布日期是200612150405秒,所以前者固定
	if err != nil {
		panic(err)
	}
	fmt.Println(t3 == t)    // true
	fmt.Println(now.Unix()) // 1648738080

注意:使用time函数前应import("time"),利用time自带函数可轻松实现格式化。

19.strconv

	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是一个字符串转换库,使用前应 import ("strconv")

20.env

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

注意:此类函数主要用于获取系统信息、路径参数等。

二、课后作业

  1. 用fmt.Scanf简化猜数游戏version5的代码。

在Golang中,fmt包是一个非常常用的包,它提供了一系列格式化输入输出的函数。其中,fmt.Scanf()函数可以从标准输入中按照指定格式读取数据。具体规则如下:

基本语法:

func Scanf(format string, a ...interface{}) (n int, err error)

格式化: fmt.Scanf()函数的格式化字符串可以包含两类内容:普通字符和格式化占位符。普通字符可以直接包含在格式化字符串中,而格式化占位符则以%开始,用于指定后续参数的格式化方式。

细节:

fmt.Scanf()函数的第一个参数format是格式化字符串,后续参数a是指针类型,用于存储读取到的数据。

fmt.Scanf()函数返回值有两个:n表示成功读取的参数个数,err表示读取过程中出现的错误,如果读取成功,则err为nil。

原代码:

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

修改后的代码:

var guess int
	for {
		_, err := fmt.Scanf("%d", &guess)
		if err != nil {
			fmt.Println("An error occured while reading input. Please try again", err)
			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
		}

	}

分析:这里主要运用scanf函数代替系统reader获取字符串再解析成对应类型的步骤。

运行结果:

image.png