简单实训——猜数、词典 | 青训营笔记

140 阅读5分钟

三个简单实训——猜数、词典、SOCKS5代理

(这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记)

一、猜数

1.整体代码

package main
​
import (
   "bufio"
   "fmt"
   "math/rand"
   "os"
   "strconv"
   "strings"
   "time"
)
​
func main() {
   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)
   for {
      input, err := reader.ReadString('\n')
      if err != nil {
         fmt.Println("An error occured while reading input. Please try again", err)
         continue
      }
      input = strings.TrimSuffix(input, "\r\n")
​
      guess, err := strconv.Atoi(input)
      if err != nil {
         fmt.Println("Invalid input. Please enter an integer value")
         continue
      }
      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.随机数的生成

maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)

当没有更改随机数时,会生成相同的随机数,需要每次启动程序时更改随机数种子。利用启动时的时间戳来初始化随机数种子。time.Now()用于获得当前的时间,返回Time类型,而UnixNano()根据所给的时间,返回从1970年1月1日开始的秒数。启动程序时,通过time.Now().UnixNano()来获得不一样的随机数种子。

3.用户输入的读取

reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil {
   fmt.Println("An error occured while reading input. Please try again", err)
   continue
}
input = strings.TrimSuffix(input, "\r\n")
guess, err := strconv.Atoi(input)

bufio模块通过对io模块的封装,提供了数据缓冲功能,能够一定程度减少大块数据读写带来的开销。实际上在bufio各个组件内部都维护了一个缓冲区,数据读写操作都直接通过缓存区进行。当发起一次读写操作时,会首先尝试从缓冲区获取数据;只有当缓冲区没有数据时,才会从数据源获取数据更新缓冲。

NewReader函数创建bufio.Reader对象,函数接收一个io.Reader作为参数。ReadString函数读取一个字符串,接受一个参数delim用于数据拆分,持续读取数据直到当前字节值等于delim。

TrimSuffix用于去除字符串中的指定后缀。Atoi将字符串转换为整型数。

var guess int _
err := fmt.Scanf("%d", &guess)

也可以使用fmt.Scanf()来进行输入读取。

二、词典

1.建立请求

①在彩云小译中进行翻译请求,找到词典请求

彩云词典请求.png

复制请求的curl,通过网站Convert curl commands to code (curlconverter.com)将命令行命令转换为go代码,便捷获得请求代码。

2.请求代码

package main
​
import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "strings"
)func main() {
    client := &http.Client{}
    var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
    req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
    if err != nil {
        log.Fatal(err)
    }
    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("Connection", "keep-alive")
    req.Header.Set("Content-Type", "application/json;charset=UTF-8")
    req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
    req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
    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/102.0.5005.0 Safari/537.36")
    req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
    req.Header.Set("app-name", "xy")
    req.Header.Set("os-type", "web")
    req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="102", "Microsoft Edge";v="102"`)
    req.Header.Set("sec-ch-ua-mobile", "?0")
    req.Header.Set("sec-ch-ua-platform", `"Windows"`)
    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)
}
client := &http.Client{}
创建http client
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
构造post类型的http请求,最后一个参数是body,因为body可能很大,为了支持流式发送,是一个只读流。我们用了strings.NewReader来把字符串转换成一个流。
var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
req.Header.Set()为请求设置header
resp, err := client.Do(req)
发送构造好的整个请求
defer resp.Body.Close()
defer语句确保函数结束时再关闭response返回的数据流

1.'defer'

defer是Go语言中的延迟执行语句,用来添加函数结束时执行的代码,常用于释放某些已分配的资源、关闭数据库连接、断开socket连接、解锁一个加锁的资源。

defer语句在函数返回之前 或者 函数中 return语句(return语句可能调用另一个函数) 之后执行。

return语句并不是一个原子操作,而是被拆分成了两步

rval=xxx
ret

而defer语句在这两条语句之间执行

rval=xxx
fefer_func
ret
当出现多条 defer 语句时以逆序执行(类似栈,即后进先出)。
func deferSample() {
    for i := 0; i < 5; i++ {
        defer fmt.Printf("%d ", i)
    }
}
上述代码将会输出:4 3 2 1 0

在panic语句后面的defer语句不被执行,在panic语句前的defer语句会被执行。Go中的panic类似其它语言中的抛出异常,panic后面的代码不再执行(panic语句前面的defer语句会被执行)。

2.'json'

一种数据存储、传输的语法格式。花括弧表示一个“容器”,方括号装载数组,名称和值用冒号隔开,数组元素通过逗号隔开。

package main
import (
   "encoding/json"
   "fmt"
)
type userInfo struct {
   Name  string
   Age   int
   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)
   fmt.Println(string(buf))
   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)
}
type Stu struct {
    Name  string `json:"name"`
    Age   int
    HIgh  bool
    sex   string
    Class *Class `json:"class"`
}
​
type Class struct {
    Name  string
    Grade int
}
​
func main() {
    //实例化一个数据结构,用于生成json字符串
    stu := Stu{
        Name: "张三",
        Age:  18,
        HIgh: true,
        sex:  "男",
    }
​
    //指针变量
    cla := new(Class)
    cla.Name = "1班"
    cla.Grade = 3
    stu.Class=cla
​
    //Marshal失败时err!=nil
    jsonStu, err := json.Marshal(stu)
    if err != nil {
        fmt.Println("生成json字符串错误")
    }
​
    //jsonStu是[]byte类型,转化成string类型便于查看
    fmt.Println(string(jsonStu))
}

①只要是可导出成员(变量首字母大写),都可以转成json。因成员变量sex是不可导出的,故无法转成json。

②如果变量打上了json标签,如Name旁边的 json:"name" ,那么转化成的json key就用该标签“name”,否则取变量名作为key,如“Age”,“HIgh”。

③bool类型也是可以直接转换为json的value值。Channel, complex 以及函数不能被编码json字符串。当然,循环的数据结构也不行,它会导致marshal陷入死循环。

④指针变量,编码时自动转换为它所指向的值,如cla变量。 (当然,不传指针,Stu struct的成员Class如果换成Class struct类型,效果也是一模一样的。只不过指针更快,且能节省内存空间。)