Go 语言基础 | 青训营笔记

126 阅读4分钟

这是我参与「第五届青训营 」笔记创作活动的第1天

一、内容概览

  • Go 语言环境搭建
    • Windows 下 Go 环境搭建
    • Linux 下 Go 环境搭建
  • Go 语言基础语法
    • Hello World!
    • 其他
  • 实战
    • 猜谜游戏
    • 在线词典
    • SOCKS5 代理

二、知识点详解

1. Go 语言环境搭建

1.1 Windows 下 Go 环境搭建

1.2 Linux 下 Go 环境搭建

  • 1.2.1 下载压缩文件
    $ wget https://studygolang.com/dl/golang/go1.19.5.linux-amd64.tar.gz
    $ ls
    go1.19.5.linux-amd64.tar.gz
    
  • 1.2.2 解压至指定路径
    $ sudo tar -C /usr/local -xzf go1.19.5.linux-amd64.tar.gz
    [sudo] password for chang:
    $ cd /usr/local
    $ ls -l
    ···
    drwxr-xr-x 10 root root 4096 Jan 10 06:41 go
    ···
    
  • 1.2.3 建立 Go 工作空间
    • src: 里面每一个子目录,就是一个包。包内是Go的源码文件
    • pkg: 编译后生成的,包的目标文件
    • bin: 生成的可执行文件
    $ mkdir go
    $ ls
    go
    $ cd go
    $ mkdir src
    $ mkdir pkg
    $ mkdir bin
    $ ls
    bin  pkg  src
    $ chmod 777 src
    $ chmod 777 pkg
    $ chmod 777 bin
    
  • 1.2.4 设置环境变量
    • 方法一
      $ vi ~/.bashrc
      # Go environment 
      export PATH=$PATH:/usr/local/go/bin
      export GOPATH=/home/chang/Projects/go
      $ source ~/.bashrc
      
    • 方法二
      $ vi /etc/profile
      # Go environment
      export GOROOT=/usr/local/go
      export GOPATH=/home/chang/Projects/go
      export PATH=$PATH:$GOROOT/bin
      $ source $HOME/.profile
      
  • 1.2.5 测试 Go 语言环境
    $ go version
    go version go1.19.5 linux/amd64
    $ go env
    ···
    
  • 1.2.6 Hello World!
    $ vi hello.go
    package main
    import "fmt"
    func main() {
        fmt.Printf("Hello World!\n")
    }
    $ go run hello.go
    Hello World!
    
  • 1.2.7 VS Code 配置

    go语言环境搭建 - 个人文章 - SegmentFault 思否

2. Go 基础语法

2.1 Hello World!

// hello.go
package main  // 指定 hello.go 文件属于 main 包
import "fmt"  // 导入标准库包
func main() { // main 函数, 程序入口 
    fmt.Printf("Hello World!\n")  // 打印 Hello World!
}

go 的文件需指定所在的包,如本例中 package main 指定了 hello.go 文件属于 main 包,main 包有唯一的 main 函数,为程序入口,只有 main 包可编译成可执行文件。

2.2 基础语法

Go by Example 中文 (studygolang.com)

  • 变量
  • if-else
  • for
  • switch
  • 数组
  • 切片
  • map
  • range
  • 函数
  • 指针
  • 结构体
  • 结构体方法
  • 错误处理
  • 字符串操作
  • 字符串格式化
  • JSON 处理
  • 时间处理
  • 数字解析
  • 进程信息

三、实践

wangkechun/go-by-example (github.com)

1. 猜谜游戏

1.1 流程

  1. 生成随机数
  2. 读取用户输入
  3. 判断逻辑
  4. 游戏循环

1.2 代码实现

package main

import (
    "bufio"
    "fmt"
    "math/rand"
    "os"
    "strconv"
    "strings"
    "time"
)

func main() {
    // 1. 生成随机数
    maxNum := 100  // 设置待猜测数字的最大值
    rand.Seed(time.Now().UnixNano())  // 设置随机数种子
    secretNumber := rand.Intn(maxNum)  // 生成随机数
    // fmt.Println("The secret number is ", secretNumber)  // 调式时查看生成的随机数

    fmt.Println("Please input your guess")  // 提示用户输入
    reader := bufio.NewReader(os.Stdin)  // 有缓冲的 I/O, 接收用户输入 【可简化】
    
    // 4. 游戏循环, 用户不断猜测直到猜正确
    for {
        // 2. 读取用户输入
        input, err := reader.ReadString('\n')  // 
        if err != nil {
            fmt.Println("An error occured while reading input. Please try again", err)
            continue
        }
        
        input = strings.Trim(input, "\r\n")  // 删除输入参数的回车、换行
        
        guess, err := strconv.Atoi(input)  // 输入字符串类型转 int
        if err != nil {
            fmt.Println("Invalid input. Please enter an integer value")
            continue
        }
        fmt.Println("You guess is", guess)
        // 3. 判断逻辑
        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
        }
    }
}

1.3 改进

fmt.Scanf 简化输入

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    var guess int
    maxNum := 100
    rand.Seed(time.Now().UnixNano())
    secretNumber := rand.Intn(maxNum)
    // fmt.Println("The secret number is ", secretNumber)
    fmt.Println("Please input your guess")
    for {
        fmt.Scanf("%d", &guess)
        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
        }
    }
}

2. 在线词典

2.1 知识点

  • Go 发送 Http 请求
  • 解析 JSON
  • curl 请求生成为 Go 代码

2.2 流程

  1. 抓包:查看彩云小译翻译时发送的 request 及 response

    image.png

  2. curl 请求生成 go 代码

    Convert curl to Go (curlconverter.com)

    package main
    
    import (
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
        "strings"
    )
    
    func main() {
        client := &http.Client{}
        var data = strings.NewReader(`{"trans_type":"en2zh","source":"hello"}`)
        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", "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7")
        req.Header.Set("app-name", "xy")
        req.Header.Set("content-type", "application/json;charset=UTF-8")
        req.Header.Set("device-id", "")
        req.Header.Set("dnt", "1")
        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="8", "Chromium";v="108", "Microsoft Edge";v="108"`)
        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/108.0.0.0 Safari/537.36 Edg/108.0.1462.76")
        req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
        resp, err := client.Do(req)
        if err != nil {
            log.Fatal(err)
        }
        defer resp.Body.Close()
        bodyText, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%s\n", bodyText)
    }
    
  3. request 的 json 序列化

    上一步生成的 go http 请求代码的输入为固定的字符串输入,需要将输入修改为变量

    type DictRequest struct {
        TransType string `json:"trans_type"`
        Source    string `json:"source"`
        UserID    string `json:"user_id"`
    }
    
    func main() {
        client := &http.Client{}
        
        // var data = strings.NewReader(`{"trans_type":"en2zh","source":"hello"}`)
        
        // json 序列化
        request := DictRequest{TransType: "en2zh", Source: "hello"}
        buf, err := json.Marshal(request)
        if err != nil {
            log.Fatal(err)
        }
        var data = bytes.NewReader(buf)
        
        req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
        ···
    }
    
  4. response 的 json 生成结构体

    JSON转Golang Struct - 在线工具 - OKTools

  5. response 的 json 反序列化

    package main
    
    import (
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
        "strings"
    )
    
    type DictRequest struct {
        TransType string `json:"trans_type"`
        Source    string `json:"source"`
        UserID    string `json:"user_id"`
    }
    
    type DictResponse struct {
        ···
    } 
    
    func main() {
        client := &http.Client{}
        
        // json 序列化
        request := DictRequest{TransType: "en2zh", Source: "hello"}
        buf, err := json.Marshal(request)
        if err != nil {
            log.Fatal(err)
        }
        var data = bytes.NewReader(buf)
        
        req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
        if err != nil {
            log.Fatal(err)
        }
        ···
        resp, err := client.Do(req)
        if err != nil {
            log.Fatal(err)
        }
        defer resp.Body.Close()
        bodyText, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            log.Fatal(err)
        }
        // fmt.Printf("%s\n", bodyText)
        
        // json 反序列化
        var dictResponse DictResponse
        err = json.Unmarshal(bodyText, &dictResponse)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
        for _, item := range dictResponse.Dictionary.Explanations {
            fmt.Println(item)
        }
    }
    

2.3 代码实现

package main

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

type DictRequest struct {
    TransType string `json:"trans_type"`
    Source    string `json:"source"`
    UserID    string `json:"user_id"`
}

type DictResponse struct {
    ···
}

func query(word string) {
    client := &http.Client{}
    
    request := DictRequest{TransType: "en2zh", Source: word}
    buf, err := json.Marshal(request)
    if err != nil {
        log.Fatal(err)
    }
    var data = bytes.NewReader(buf)
    req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
    if err != nil {
        log.Fatal(err)
    }
    ···
    resp, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    bodyText, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    if resp.StatusCode != 200 {
        log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
    }
    
    var dictResponse DictResponse
    err = json.Unmarshal(bodyText, &dictResponse)
    if err != nil {
        log.Fatal(err)
    }
    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.Fprintf(os.Stderr, `usage: simpleDict WORD example: simpleDict hello`)
        os.Exit(1)
    }
    word := os.Args[1]
    query(word)
}

2.4 改进

  • 增加另一种翻译引擎的支持
  • 并行请求两个翻译引擎提高响应速度

3. SOCKS5 代理

四、总结

  • Go 基础语法
  • Go 发送 Http 请求
  • curl 请求生成 go 代码
  • json 生成 go 结构体
  • SOCKS5 协议

五、参考