这是我参与「第五届青训营 」笔记创作活动的第1天
今天一共有三部分内容,go语言介绍,go语言基础语法以及三个项目实战。由于go语言介绍为了解内容,项目实战是对go语言基础语法的运用,所以本篇笔记主要是进行语法的记录
1.go语言语法
1.1变量
go语言常见的变量类型包括 字符串 整数 浮点型、布尔型等。 go 语言的字符串是内置类型,可以直接通过加号拼接,也能够直接用等于号去比较两个字符串。在go语言里面变量的声明有两种方式:
var name string = ""
使用变量 冒号 := 等于值。
常量的话就是把 var 改成const,go语言里面的常量没有确定的类型,会根据使用的上下文来自动确定类型。
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) // initial 1 2 true 0 0
fmt.Println(g) // initialapple
const s string = "constant"
const h = 500000000
const i = 3e20 / h
fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}
1.2 if else,switch,map
- if else:写法和 C 或者 C++ 类似。不同点1是 if 后面没有括号。第二个不同点是 Golang 里面的if ,它必须后面接大括号
- switch :后面的那个变量名,并不是要括号。在c++里面, switch case 如果不不显示加 break 的话会然后会继续往下跑完所有的 case, 在go语言里面的话是不需要加 break 的。可以使用任意的变量类型可以在 switch 后面不加任何的变量,然后在 case 里面写条件分支
- map :可以用 make 来创建一个空 map ,这里会需要两个类型,第一个是那个 key 的类型,另一个是 value 的类型。 我们可以从里面去存储或者取出键值对。可以用 delete 从里面删除键值对。 golang的map是完全无序的,遍历的时候不会按照字母顺序,也不会按照插入顺序输出,而是随机顺序。
1.3 数组和切片
在写代码时很少直接使用数组,因为它长度是固定的,我们用的更多的是切片。切片不同于数组可以任意更改长度,然后也有更多丰富的操作。比如说我们可以用 make 来创建一个切片,可以像数组一样去取值,使用 append 来追加元素。 注意 append 的用法的话,你必须把 append 的结果赋值为原数组。在执行 append 操作的时候,如果容量不够的话,会扩容并且返回新的 slice。 slice 此初始化的时候也可以指定长度。 slice 拥有像 python 一样的切片操作,比如这个代表取出第二个到第五个位置的元素,不包括第五个元素。不过不支持负数索引
slice实例:
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]
}
1.4 错误处理
在 go 语言里面符合语言习惯的做法就是使用一个单独的返回值来传递错误信息。 不同于 Java 自家家使用的异常。go语言的处理方式,能够很清晰地知道哪个函数返回了错误,并且能用简单的 if else 来处理错误。 在函数里面,我们可以在那个函数的返回值类型里面,后面加一个 error, 就代表这个函数可能会返回错误。 那么在函数实现的时候, return 需要同时 return 两个值,要么就是如果出现错误的话,那么可以 return nil 和一个 error。如果没有的话,那么返回原本的结果和 nil。
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)
}
}
1.5 json处理
go语言 里面的 JSON 操作非常简单,对于一个已有的结构体,我们可以什么都不做,只要保证每个字段的第一个字母是大写,也就是是公开字段。那么这个结构体就能用 JSON.marshaler 去序列化,变成一个 JSON 的字符串。 序列化之后的字符串也能够用 JSON.unmarshaler 去反序列化到一个空的变量里面。 这样默认序列化出来的字符串的话,它的风格是大写字母开头,而不是下划线。我们可以在后面用 json tag 等语法来去修改输出 JSON 结果里面的字段名。
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"}}
}
还有部分内容如指针,结构体,循环,字符串的操作等内容与其他语言区别并不大,不过多解释,可以进行代码参考。
2.项目实战总结
- 猜谜游戏
主要是对if else 和 Switch 的运用(输入可以使用 %scanf) - 命令行词典
对结构体的运用和学习如何用 go 语言来 来发送 HTTP 请求 、 解析json 过来, 学习如何使用代码生成来提高开发效率。浏览器会发送一系列请求,我们能很轻松地找到那个用来查询单词的请求。然后请求头是一个 json 里面有两个字段,一个是代表你要你是从什么语言转化成什么语言,source 就是你要查询的单词。 API 的返回结果里面会有 Wiki 和 dictionary 两个字段。我们需要用的结果主要在dictionary.Explanations 字段里面。
代码生成网站:curlconverter.com/#go
解析response body:oktools.net/json2go - SOCKS5代理
- socks5 协议的工作原理。正常浏览器访问一个网站,如果不经过代理服务器的话,就是先和对方的网站建立 TCP 连接,然后三次握手,握手完之后发起 HTTP 请求,然后服务返回 HTTP 响应。如果设置代理服务器之后,流程会变得复杂一些。 首先是浏览器和 socks5 代理建立 TCP 连接,代理再和真正的服务器建立 TCP 连接。这里可以分成四个阶段,握手阶段、认证阶段、请求阶段、 relay 阶段。
- 第一个握手阶段,浏览器会向 socks5 代理发送请求,包的内容包括一个协议的版本号,还有支持的认证的种类,socks5 服务器会选中一个认证方式,返回给浏览器。如果返回的是 00 的话就代表不需要认证,返回其他类型的话会开始认证流程,这里我们就不对认证流程进行概述了。
- 第三个阶段是请求阶段,认证通过之后浏览器会 socks5 服务器发起请求。主要信息包括 版本号,请求的类型,一般主要是 connection 请求,就代表代理服务器要和某个域名或者某个 IP 地址某个端口建立 TCP 连接。代理服务器收到响应之后,会真正和后端服务器建立连接,然后返回一个响应。
- 第四个阶段是 relay 阶段。此时浏览器会发送 正常发送请求,然后代理服务器接收到请求之后,会直接把请求转换到真正的服务器上。然后如果真正的服务器以后返回响应的话,那么也会把请求转发到浏览器这边。然后实际上 代理服务器并不关心流量的细节,可以是 HTTP流量,也可以是其它 TCP 流量。 这个就是 socks5 协议的工作原理,接下来我们就会试图去简单地实现它。
本篇笔记主要参考:
go语言上手(青训营课件)