Go学习之路 | 青训营

68 阅读4分钟

基础语法、实战案例

1. 基础语法中值得注意的地方

  • go是一门强类型语言:变量的使用要严格符合定义,先定义后使用。
  • 没有while循环。
  • 取地址符是&,声明指针是*,与C一致。
  • 声明变量的时候要注意什么时候使用:=
var (  // 这种因式分解关键字的写法一般用于声明全局变量  intbool  
)
  • 函数返回值可以有多个。
  • 切片slice和Python类似,支持动态扩容,append追加元素。 s := make([], string, 3)
  • 异常处理的机制:以前学过一点C#,是用try, catch实现异常处理的,感觉比较繁琐,有时候不知道将哪一部分包起来好。Go的异常处理感觉就简单清晰很多,感觉比较舒服。

入门小项目1:在线词典-抓包

基本操作如下:
  • 彩云翻译抓取cURL,丢到Converter里转化成go源码,成功运行后会输出JSON字符串
  • 为了实现获取动态输入,要做JSON序列化:构造相应结构体即可,在此处可以做在线代码生成。
  • 解析获取的response body同理,具体位置在:点击“翻译”,右键网页-检查-network-dict(注意是post类型的请求)-preview

代码块

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
)

// 实现获取动态输入,要做JSON序列化:构造相应结构体即可
type DictRequest struct {
	TransType string `json:"trans_type"`
	Source    string `json:"source"`
	UserID    string `json:"user_id"`
}

type DictResponse struct {
	Rc   int `json:"rc"`
	Wiki struct {
	} `json:"wiki"`
	Dictionary struct {
		Prons struct {
			EnUs string `json:"en-us"`
			En   string `json:"en"`
		} `json:"prons"`
		Explanations []string      `json:"explanations"`
		Synonym      []string      `json:"synonym"`
		Antonym      []string      `json:"antonym"`
		WqxExample   [][]string    `json:"wqx_example"`
		Entry        string        `json:"entry"`
		Type         string        `json:"type"`
		Related      []interface{} `json:"related"`
		Source       string        `json:"source"`
	} `json:"dictionary"`
}

func query(word string) {
	// 可以指定一些重要参数,比如timeout
	client := &http.Client{}
	request := DictRequest{TransType: "en2zh", Source: word}

	buf, err := json.Marshal(request)
	if err != nil {
		log.Fatal(err)
	}

	var data = bytes.NewBuffer(buf)

	// 以流传输,减少内存占用
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("authority", "api.interpreter.caiyunai.com")
	req.Header.Set("accept", "application/json, text/plain, */*")
	req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
	req.Header.Set("app-name", "xy")
	req.Header.Set("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "2a717e9a6a196072ed3294ce3d8c19f6")
	req.Header.Set("origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"`)
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("sec-ch-ua-platform", `"Windows"`)
	req.Header.Set("sec-fetch-dest", "empty")
	req.Header.Set("sec-fetch-mode", "cors")
	req.Header.Set("sec-fetch-site", "cross-site")
	req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183")
	req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")

	// 真正发起的请求
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}

	// 关闭流,防止泄露
	defer resp.Body.Close()
	bodyText, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	// 防御式编程
	if resp.StatusCode != 200 {
		log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
	}

	// 将得到的bodyText反序列化
	var dictResponse DictResponse
	err = json.Unmarshal(bodyText, &dictResponse)
	if err != nil {
		log.Fatal(err)
	}

	// #v理解为全部打印, Printf打印带格式化的字符串,Println打印字符串和变量
	// fmt.Printf("%#v\n", dictResponse)

	// 只打印需要的信息
	fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
	for _, item := range dictResponse.Dictionary.Explanations {
		fmt.Println(item)
	}
}

func main() {
	if len(os.Args) != 2 {
		// fmt.Printf(os.Stderr, `usage: simpleDict WORD example: simpleDict hello`)
		os.Exit(1)
	}
	word := os.Args[1]
	query(word)
}

入门项目2.Socks5代理

  • SOCKS5代理看上去很复杂,实际上它只是IP代理协议中的一种,同时又是最重要、最流行的一种,真正的匿名代理只能由SOCKS5协议的代理IP来实现。
  • 介绍了代理的运行原理。
  • 一个服务项目:TCP echo server: 监听,并原路返回收到的信息的TCP server。使用nc命令要用到netcat工具,注意windows自带的杀软会被误报病毒,要添加白名单。

最后来试着用Go写一个LeetCode算法题

[题目链接](1246. 删除回文子数组 - 力扣(LeetCode)

解题方法

区间DP的套路个人感觉比较固定,通常状态都表示为f[i][j]f[i][j]:使区间[i,j][i,j]满足条件的最小操作数,注意是闭区间。初始化f[i][i]f[i][i]11,其余为正无穷。状态转移时第一重循环枚举左端点,第二重循环枚举数组长度。

考虑状态转移的几种情况:

Case1:

a[i]a[j]a[i] \neq a[j],则f[i][j]=1+min(f[i+1][j],f[i][j1])f[i][j]=1+min(f[i+1][j],f[i][j-1]) 否则f[i][j]=f[i+1][j1]f[i][j]=f[i+1][j-1],注意特判越界的情况

Case2

整个数组由几个回文子数组构成,如arr=[1,4,1,1,2,3,2,1]arr=[1,4,1,1,2,3,2,1] 此时要枚举一下中间的分割点kk,f[i][j]=min(f[i][j],f[i][k]+f[k+1][j])f[i][j]=min(f[i][j],f[i][k] + f[k+1][j])

Code

func minimumMoves(arr []int) int {
    var n := len(arr)
	var dp [101][101]int
	for i := 0; i < len(arr); i++ {
		dp[i][i] = 1
	}
	for i := 0; i < len(arr)-1; i++ {
		if arr[i] == arr[i+1] {
			dp[i][i+1] = 1
		} else {
			dp[i][i+1] = 2
		}
	}
	for l := 2; l < len(arr); l++ {
		for i := 0; i+l < len(arr); i++ {
			j := i + l
			dp[i][j] = l + 1
			for k := 0; k <= l; k++ {
				if dp[i][i+k]+dp[i+k+1][j] < dp[i][j] {
					dp[i][j] = dp[i][i+k] + dp[i+k+1][j]
				}
			}
			if arr[i] == arr[j] {
				if dp[i+1][j-1] < dp[i][j] {
					dp[i][j] = dp[i+1][j-1]
				}
			}
		}
	}
	return dp[0][len(arr)-1]
}

总结

  • Socks5的项目其实还没有实现,仅仅只是实现了一个CP echo server的监听服务。因为对Socks5这个概念的了解还比较模糊,待后续进一步学习再回头来看这个项目。
  • “抓包”这个概念虽然已经在很多地方都听到过了,但实战起来还是第一次,有了比较初步的认识。
  • 做这个小项目的同时,初步学习了GO语言的基础语法。以前只学过C++和Python这类面向对象思想的语言,所以第一次上手接触还是感到非常新奇的。