GO基础语法 | 青训营笔记

203 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第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函数

image.png

指针

  • 表示变量的地址
  • 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)

简单词典

socks5代理原理

关于本内容,以后会再专门写一篇笔记进行学习

  1. 协商阶段
  2. 请求阶段
    • 主机向代理发送请求
    • 代理服务器与服务器建立TCP链接
  3. 发送数据 代理服务器转发数据到服务器

image.png

以下为SOCKS5实现代理,解析数据时所用到的协议格式

image.png

image.png

image.png