三个简单实训——猜数、词典、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.建立请求
①在彩云小译中进行翻译请求,找到词典请求
复制请求的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类型,效果也是一模一样的。只不过指针更快,且能节省内存空间。)