这是我参与「第三届青训营 -后端场」笔记创作活动的的第一篇笔记
我是纯纯的Go小白,在青训前也没怎么听过它,总感觉高深莫测但当我真正进入青训后才发现它语法简单,而且具有高性能,高并发,快速编译,跨平台等诸多优点,简直一整个爱上了它,ヾ(≧▽≦*)o下面就来写写我第一节课学到的东西。
1.基础语法
go语言里的变量声明很适合我这样的懒人,两种方式都可以自动推导类型,当然要是有需要你也可以自己显示写出。go语言里字符串是内置类型,可以直接通过加法拼接,也可直接去用等号比较两个字符串。值得注意的是go语言里的常量,它没有确定的类型,会根据使用的上下文来自动确定。
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) // initial 1 2 true 0 0
fmt.Println(g) // initialfoo
const s string = "constant"
const h = 500000000
}
在go语言里if后面没有小括号,如果你写了那么在保存时也会被编辑器自动删掉,而其后面必须接大括号, 不能直接将内容于if同行。
对了,go语言不会养闲人,对于你导入的包以及声明的变量之类的都必须用到,否则就会导致编译失败。同时go语言还比较有个性,在循环这里只相下了for循环,while和do while都没有生存之地。也可能是被switch吸取了功力,go里的switch功能十分强大,可以使用任意的变量类型,甚至可以用来取代任意的if else语句。
切片是go语言里一个重要的基础概念,它不同于数组可以任意改变长度。我们可以使用make来创建一个切片,可以像数组一样取值,也可用append来追加元素。然我们必须得把append的结果赋值给原数组,因为其实际上是有一个存储了一个长度和一个容量,加一个指向数组的指针,当执行append操作时,如果容量不够,会进行扩容并返回新的切片。
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]
而对于map,我们也可以用make来创建,这里需要两个类型,第一个是key的类型,此处是string,另一个是value的类型,此处是int。
m := make(map[string]int)
m["one"] = 1
m["two"] = 2
fmt.Println(m) // map[one:1 two:2]
range遍历时,对于数组会返回两个值,第一个是索引,第二个对应位置的值,如果不需要索引的话,我们也可以用下划线来忽略。
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
}
此外,go语言最我惊喜的就是函数原生支持返回多个值,在之前写c++时我就曾想过返回多个值,没有想到能够在这里实现。go函数两个值,第一个是真正的返回结果,第二个是一个错误信息。
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里的指针和结构体和c里都比较像,就不赘述了,直接来看结构体方法,类似于其他语言的类成员函数,改为结构体方法后,用户可以像a.checkPassword("xx")这样调用。
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
}
错误处理这里,先引入一下nil的概念。
在Go语言中,布尔类型的零值(初始值)为 false,数值类型的零值为 0,字符串类型的零值为空字符串
"",而指针、切片、映射、通道、函数和接口的零值则是 nil。nil 是Go语言中一个预定义好的标识符,有过其他编程语言开发经验的开发者也许会把 nil 看作其他语言中的 null(NULL),但其实这并不是完全正确的。
在函数实现时,return需要返回两个值,如果出现错误,就return nil和一个error,如果没有则返回原本的结果和nil。
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
}
在string包中包含很多字符串操作方法,同时有很多字符串格式化相关方法。不过,在go语言里,可以使用%v来打印任意类型的变量,而不用区分数字字符串,也可用%+v打印详细结果,%#v则更详细。
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
}
下面来看JSON操作, go语言里面的JSON操作非常简单,对于一个已有的结构体,我们可以什么都不做,只要保证每个字段的第一个字母是大写,也就是是公开字段。那么这个结构体就能用JSON.marshaler去序列化,变成JSON的字符串。
序列化之后的字符串也能够用JSON.unmarshaler去反序列化到一个空的变量里面。这样默认序列化出来的字符串的话,它的风格是大写字母开头,而不是下划线。我们可以在后面用json tag等语法来去修改输出JSON结果里面的字段名。
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"}}
}
时间处理
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
}
数字解析,字符串和数字之间的转换。在go语言当中,关于字符串和数字类型之间的转换都在STRcow这个包下,我们可以用parselnt 或者parseFloat来解析—个字符串。我们可以用Atoi把一个十进制字符串转成数字。可以用itoA把数字转成字符串。如果输入不合法,那么这些函数都会返回error。
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
}
关于进程信息,在go里面,我们能够用os.argv来得到程序执行的时候的指定的命令行参数。 比如我们编译的一个二进制文件,command。后面接abcd 来启动,输出就是os.argyv会是一个长度为5的sice ,第一个成员代表二进制自身的名字。我们可以用so.getenv来读取环境变量。
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
}
2.猜谜游戏实战演练
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
maxNum := 100
rand.Seed(time.Now().UnixNano()) //随机数种子
secretNumber := rand.Intn(maxNum) //生成随机数
fmt.Println("Please input your guess")
for {
var guess int
_, err := fmt.Scanf("%d", &guess)
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
}
}
}