这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
GO 基础
GO基础语法
命令行运行
- go run 运行
- go build 编译二进制文件
基本输出 fmt包
- printf
- 通常Printf格式化字符串包含多个%参数时将会包含对应相同数量的额外操作数,但是%之后的[1]副词告诉Printf函数再次使用第一个操作数。
- %后的#副词告诉Printf在用%o、%x或%X输出时生成0、0x或0X前缀。
- %T输出类型
- %v输出任意类型
- Println 输出一行 可以携带任意多参数 以空格间隔
// import语句必须更在package后
import (
"fmt"
)
...
fmt.Println("hello world")
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}
//printf格式化输出
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}
// +v
fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
// 输出结构体时会添加字段名
fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
// 值的Go语法表示
f := 3.141592653
fmt.Println(f) // 3.141592653
fmt.Printf("%.2f\n", f) // 3.14
}
变量
- 类型
- 基本数据类型
- 整数 int8 int16 int32 int64
- 浮点数 float32 float64 32位精确表示的位数较小 优先用64
- 复数 complex
- 布尔型
- 字符串:不可改变的字节序列
- len返回的是字节数而不是字符数
- 索引返回的是第i个字节值
- 复合数据类型
- 数组
- 结构体
- Slice
- Map
- JSON
- ...
- type 可以声明一个新类型 type newType floor
- 数值类型之间可以进行类型转换
- 基本数据类型
type a int
type b int
func d() {
var x a
x = 1
var y b
y = 1
// 编译出错
x = y
}
- 变量声明
- var x type = value type/value可以省略一个
- x := value 简略写法 用于局部变量
- const x type = value 常量 type可省略
- 同时声明多个常量时 可以使用 iota 之后的常量不需要声明值 直接累加
const( BEIJING iota*10 // 0 SHANGHAI //10 NANJING //20 ) - 多变量声明
- 变量名使用 _ 可以不使用
- var x1,x2 type
- var x1,x2 = v1,v2
var ( x1 int y1 int ) - 包一级声明的变量在包内可见,与声明的位置无关,如果新创建的类型名字首字母大写,则在包外也可以使用
- 变量携带的信息 pair
- pair由两部分组成 {type: value:}
- 记录了变量的类型与值
- 当把变量赋值给另一个类型变量时,pair会传递(可以实现多态)
控制语句
- 可以不写小括号 但是大括号需要紧跟上一个语句
- 上一点的原因在于编译器把特定符号的换行符转换为分号
for
for {
fmt.Println("loop")
break
}
for j := 7; j < 9; j++ {
fmt.Println(j)
}
for i <= 3 {
fmt.Println(i)
i = i + 1
}
if else else/else if (紧跟前一个大括号)
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")
}
switch
不会像c++一样自动执行下一个case ;可以不写判断的变量 ;case 支持更复杂的写法,可以在switch中写判断语句 达到if else if 的效果
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")
}
数组
- var a [5]int
- var a[5][5] int
- 初值为0值
- 访问方式同c语言 固定数组的数据类型问题:不同长度的数组的数据类型不一样,且传给函数是一种值传递,所以一般使用切片
切片
- 声明切片 使用make/或者[]type
- 注意:与python不太相同,slic[:]并不是深拷贝 想要副本需要
copy - 切片传递的是指针
- 获得容量用
cap(s) - 容量增加用
s = append(s,val)
s := make([]string, 3)
// 动态添加元素
s = append(s, "d")
s = append(s, "e", "f")
c := make([]string, len(s))
// 复制切片 深拷贝
copy(c, s)
// 同Python一样的切片操作
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"}
map
- m:=make(map[string]int)
- 也可以 m := map[string]int{}
- 添加key可以直接添加 如同python
- map传递的是指针
m := make(map[string]int)
m["one"] = 1
fmt.Println(m)
fmt.Println(len(m)) // 2
fmt.Println(m["one"]) // 1
r, ok := m["unknow"] // 不存在的值
fmt.Println(r, ok) // 0 false
// 删除一个key
delete(m, "one")
// 初始化其他方式
m2 := map[string]int{"one": 1, "two": 2}
var m3 = map[string]int{"one": 1, "two": 2}
range
可以与for一起使用进行遍历 第一个是index/key 第二个是value
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
}
函数
- 首字母的大小写决定访问权限
- func name(params)return{} 返回值可以有多个
- 返回值也可以命名 这样就只需要写return
func exists(m map[string]string, k string) (v string, ok bool) {
v, ok = m[k]
return v, ok
}
func exists(m map[string]string, k string) (v string, ok bool) {
v, ok = m[k]
return
}
init函数
- init函数可以用于初始化操作 其执行早于main函数
指针
- 表示变量的地址
- var p *int = &n
- 可以作为函数的参数与返回值
- 可以直接操作结构体内的函数
- *p 代表所指的变量的值
结构体
-
如果类名首字母大写 则包外可访问 如果类中字段首字母大写 则属性对外部可访问
-
传参并不会复制,除非传的是地址
-
结构体方法
func (x struct/*struct) name(parmas) return{}注意结构体方法有指针与非指针两种 -
结构体内的内容用 var. 来使用
-
继承的方式: 在结构体中 写一下类型
- 可以重写父类中的方法
type parent struct {
name string
}
type child struct {
parent
age int
}
// 声明一个结构体
type user struct {
name string
password string
}
func main() {
//声明方式1
a := user{name: "wang", password: "1024"}
c := user{name: "wang"}
c.password = "1024"
//声明方式2
var d user
d.name = "wang"
d.password = "1024"
fmt.Println(a, b, c, d) // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
}
func (u user) checkPassword(password string) bool {
return u.password == password
}
func (u *user) resetPassword(password string) {
u.password = password
}
接口
- go语言想要实现多态 可以使用接口
- 只要有一个类实现了所有接口里的方法 则他就实现了接口
- 接口本质是一个指针
type Animal interface {
Sleep()
}
type Dog struct {
name string
}
func (this *Dog) Sleep() {
fmt.Println(this.name+" sleep")
}
func main() {
var animal Animal
animal = &Dog{"petty"}
animal.Sleep()
}
空接口 万能类型
- interface{} 别名 any
- 任何类型都实现了空接口
- 可以用空接口引用任意类型
空接口的使用场景
- ⽤空接⼝表示任意数据类型,类似于java中的object。
- println的参数就是空接⼝;
- 定义⼀个map: key是string, value是任意数据类型;
- 定义⼀个切⽚,其中存储任意类型的数据。
//1. println的参数就是空接口
fmt.Println("println的参数可以是任意类型,用空接口表示\n", 100,
3.14, Cat{"小天", 2})
//2. 定义一个map: key是string,value是任意类型
map1 := make(map[string]interface{})
map1["name"] = "Daniel"
map1["age"] = 13
fmt.Println(map1)
fmt.Println("--------------------")
//3.定义一个切片,其中可以存储任意类型的数据
slice1 := make([]interface{}, 0, 10)
slice1 = append(slice1, a1, a2, a3, a4, a5)
fmt.Println(slice1)
错误处理
- 对于大部分函数而言,永远无法确保能否成功运行。
- 在Go的错误处理中,错误是软件包API和应用程序用户界面的一个重要组成部分,程序运行失败仅被认为是几个预期的结果之一
- 对于那些将运行失败看作是预期结果的函数,它们会返回一个额外的返回值,通常是最后一个,来传递错误信息。
// 如果没有错误 返回nil 否则返回错误(实现error接口)
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")
}
字符串操作
- 常用函数 引入strings
- 字符串格式化
- 以下列出了常用函数
import (
"fmt"
"strings"
)
func main() {
a := "hello"
fmt.Println(strings.Contains(a, "ll")) // 子串
fmt.Println(strings.Count(a, "l")) // 计数
fmt.Println(strings.HasPrefix(a, "he")) // 以xx为前缀
fmt.Println(strings.HasSuffix(a, "llo")) // 以xx为后缀
fmt.Println(strings.Index(a, "ll")) // 首次出现下标
fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // 数组转字符串
fmt.Println(strings.Repeat(a, 2)) // 重复 返回结果
fmt.Println(strings.Replace(a, "e", "E", -1)) // 替换 最后一个数字代表前n个 负数为所有
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
}
json操作
- 导入包: encoding/json
- 基本的json类型有字符串 布尔值 数字
- json对象类型可以用于编码Go语言我的map(key是字符串) 和结构体
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"}}
// 转为json 此时是字节数组
buf, err := json.Marshal(a)
if err != nil {
panic(err)
}
fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}
// 格式化
buf, err = json.MarshalIndent(a, "", "\t")
fmt.Println(string(buf))
var b userInfo
// 字符串转为struct
err = json.Unmarshal(buf, &b)
fmt.Printf("%#v\n", b)
}
时间处理
- 导入包 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")
if err != nil {
panic(err)
}
fmt.Println(t3 == t) // true
fmt.Println(now.Unix()) // 1648738080
数字解析
- 导入包 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
//Atoi is equivalent to ParseInt(s, 10, 0), converted to type int.
n2, _ := strconv.Atoi("123")
fmt.Println(n2) // 123
//报错
n2, err := strconv.Atoi("AAA")
fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
}
实战部分
本篇只作简单记录
猜谜游戏
NewScanner是一个很方便的读取数据的方式。可以按照什么样的方式读取数据,比如不按照行读取,按照单词读取。只需要 在scan前加入 "input.Split(bufio.ScanWords)” 即可
input := bufio.NewScanner(os.Stdin)
input.Scan()
guess,err := strconv.Atoi(input.Text())
if(err!=nil){
log.Fatal(err)
}
在Go语言中,fmt软件包使用与C的printf()和scanf()函数相似的函数来实现格式化的I /O。 Go语言中的fmt.Scanf()函数扫描标准输入中给定的输入文本,从中读取内容,并将连续的空格分隔值存储到由格式确定的连续参数中。此外,该函数在fmt包下定义。在这里,您需要导入“fmt”包才能使用这些函数。
var guess int
fmt.Scanf("%d",&guess)
简单词典
- 生成发送http请求接受响应的go代码程序网站 curlconverter.com/#go
- 由json生成结构体网站 oktools.net/json2go
- 其余部分实现比较简单 不再说明
socks5代理原理
关于本内容,以后会再专门写一篇笔记进行学习
- 协商阶段
- 请求阶段
- 主机向代理发送请求
- 代理服务器与服务器建立TCP链接
- 发送数据 代理服务器转发数据到服务器
以下为SOCKS5实现代理,解析数据时所用到的协议格式