go语言入门与实战 (下)| 青训营
这是我在字节跳动青训营学习的第5天,也是我参加《第六届青训营笔记伴读》的第二篇笔记
概要
本文主要包括上文未写完的基础语法,以及一些实战案例
基础语法
1. 错误处理
go语言里可以用一个单独的返回值来传递错误信息,只需要在函数的返回值类型里面加一个error
,代表这个函数可能返回错误,在实现的时候,return需要返回两个值,一个是函数的原来的结果,一个是error
,这样能很清晰的知道哪个函数返回了错误,,并且能用减简单的if else
来处理错误,但同时也带来一个问题(对我而言),大量的if
语句来判断错误,影响了代码可读性
go
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")
字符串操作
go语言标准库strings包里有很多常用的字符串工具函数
- contanis 判断一个字符串里是否包含另一个字符串
go
fmt.Println(strings.Contains("hello","ll") ) //true
- count 统计字符串中出现的次数
go
fmt.Println(strings.Count("hello","ll") ) //1
- index 查找某个位置的字符串
go
fmt.Println(strings.Index("hello","ll") ) //2
- join 连接多个字符串
go
fmt.Println(strings.Join([]string{"he","llo"},"-" )) //he-llo
*repeat 重复多个字符串
go
fmt.Println(strings.Repeat("hello",2) //hellohello
- replace 替换字符串
go
fmt.Println(strings.Replace("hello","e","E",-1)) //hEllo
*len 获取字符串长度,中文长度与英文不一样
go
fmt.Println(len("hello")) //5
fmt.Println(len("你好")) //6
字符串格式化
在标准库FMT包里有很多字符串格式相关的方法,比如printf
类似于c语言中的printf
函数,不同的是,在golang中,你可以用%v
来打印任意类型 的变量,%+v
可以打印详细结果,%#v
更详细
go
type point struct{
x,y int
}
s:="hello"
n:=123
p := point{1,2}
fmt.Printf("s=%v\n",s) //s=hello
fmt.Printf("n=%v\n",n) //n=123
fmt.Printf("p=%+v\n",p) //p={x:1 y:2}
fmt.Printf("p=%#v\n",p) //p=main.point{x:1, y:2}
JSON处理
序列化
go语言里的JSON操作非常简单,对于一个已有的结构体,我们可以什么都不做,只要保证每个字段的第一个字母是大写的,也就是公开字段,这个结构体就能用JSON.marshal去序列化,变成一个JSON字符串,我们可以用json tag等语法来修改输出JSON结果里面的字段名
反序列化
使用JSON.Unmarshal就可以实现反序列化
代码实现
go
type userInfo struct {
Name string
Age int
Hobby []string
}
func main() {
a := userInfo{Name: "wang", Age: 18, Hobby: []string{"golang", "java"}}
buf, err := json.Marshal(a)
if err != nil {
panic(err)
}
fmt.Println(buf) //16进制编码
fmt.Println(string(buf)) //{"Name":"wang","Age":18,"Hobby":["golang","java"]}
//反序列化
var b userInfo
err = json.Unmarshal(buf, &b)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", b) //{wang 18 [golang java]}
}
时间处理
go语言里最常用的就是time.now()来获取当前时间,也可以用time.date构造一个带时区的时间,可以用.sub
去对两个时间进行减法,得到一个时间段,时间段又可以得到时分秒
在和某些系统交互的时候,我们经常会用到时间戳,可以用.UNIX
来获取时间戳,time.format time.parse
go
func main() {
now := time.Now()
fmt.Println(now) //2023-07-29 11:02:48.3033439 +0800 CST m=+0.002110601
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.Minute()) //2022 25
fmt.Println(t.Format("2006-01-02 15:04:05")) //2022-03-27 01:25:36
diff := t2.Sub(t)
println(diff) //3900000000000
println(diff.Minutes()) //+6.500000e+001
t3, _ := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
println(t3 == t) //true
println(now.Unix()) //1690599768
}
数字解析
在go语言中,关于自付出啊内核数字类型之间的转换都在STRconv
这个包下,这个包名是string
convert
两个单词的缩写,
我们可以用parseInt或者parseFLoat来解析一个字符串
可以用Atoi把一个十进制字符串转成数字,itoA把数字转成字符串,如果输入不合法,这些函数都会返回error
go
func main() {
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) //1.234
n, _ := strconv.ParseInt("111", 10, 64)
fmt.Println(n) //111
n2, _ := strconv.Atoi("123")
fmt.Println(n2) //123
n3, err := strconv.Atoi("AAA")
if err != nil {
fmt.Println(err) //strconv.Atoi: parsing "AAA": invalid syntax
}
fmt.Println(n3) //0
}
实战--在线词典(火山翻译)
用户可以在命令行里查询一个单词,我们通过调用第三方API查询到单词的翻译并打印出来
抓包
打开火山翻译网页,找到用来查询单词的请求,
这是一个post
请求,请求的header
相当复杂,但其实需要header
的就几个,它的请求里有四个参数分别是翻译来源,单词,原始语言,目标语言
我们需要在golang里发送这个请求,因为这个请求比较复杂, 手写代码比较麻烦,我们可以右键复制这个url,然后打开curlconverter.com/go/ ,将复制好的url粘贴进去,它会自动帮我们生成请求代码
在生成的代码中帮我们创建了一个HTTP Client
,接下来是构造post请求,用到了HTTP.NewRequest
,第一个参数是请求方法POST
,第二个参数是url
,第三个参数是body
,body
因为可能很大, 为了支持流式发送,是一个只读流,我们用strings.NewReader
把字符串转换成流,接下来就是对HTTP request
设置header
,设置完成后,调用client.do(request)
发送请求,若失败的话error
会返回非nil
,response
有HTTP
状态码,响应头,响应体(body),body
同样是一个流,为了避免资源泄露,需要加一个defer
来手动关闭这个流,defer
会在函数运行结束后执行,接下来就是用ioutil.ReadAll
来读这个流,这样我们就得到了整个body
但目前我们的输入是固定的,我们的目标是获取用户输入,然后拼接url,此时需要用到JSON的序列化,在golang中,我们需要生成一段JSON
,常用的方式是先构造一个结构体,这个结构体和我们需要生成的JSON结构是一一对应的,requestBody
中包含四个字段,分别对应四个参数
go
type requestBody struct {
Source string `json:"source"`
Words []string `json:"words"`
SourceLanguage string `json:"source_language"`
TargetLanguage string `json:"target_language"`
}
之后我们从命令行读入用户输入的数据,创建一个结构体,调用JSON.Marshal
得到序列化的字符串,但这里是个字符数组,所以我们把strings.NewReader
变成bytes.NewReader
,至此我们的请求构造完成
go
var sourceStr string
fmt.Scanf("%v", &sourceStr)
request := requestBody{
Source: "youdao",
Words: []string{sourceStr},
SourceLanguage: "zh",
TargetLanguage: "en",
}
buf, err := json.Marshal(request)
if err != nil {
log.Fatal(err)
}
var data = bytes.NewReader(buf)
接下来我们解析响应数据,同样要写一个结构体,把返回的JSON数据反序列化到结构体里面,但这个API返回的结构非常复杂,如果要一一定义结构体字段,非常繁琐且容易出错,,我们可以打开oktools.net/json2go 将json字符串粘贴进去,直接生成对应的结构体
go
type DictResponse struct {
Details []struct {
Detail string `json:"detail"`
Extra string `json:"extra"`
} `json:"details"`
}
type DictResponseData struct {
Result []struct {
Ce struct {
Basic struct {
Explains []struct {
Text string `json:"text"`
Pos string `json:"pos"`
Trans string `json:"trans"`
} `json:"explains"`
} `json:"basic"`
} `json:"ce"`
} `json:"result"`
}
然后我们定义一个结构体对象,用JSON.Unmarshal把body反序列化到结构体里面,提取出翻译的结果
go
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
item := dictResponse.Details[0]
jsonStr := item.Detail //获取到需要的数据对应的json字符串
var myData DictResponseData
err2 := json.Unmarshal([]byte(jsonStr), &myData) //将json字符串解析为结构体
if err != nil {
log.Fatal(err2)
}
//遍历输出结果
for _, item := range myData.Result[0].Ce.Basic.Explains {
fmt.Println(item.Text, item.Pos, item.Trans)
}
完整代码如下
go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
)
type requestBody struct {
Source string `json:"source"`
Words []string `json:"words"`
SourceLanguage string `json:"source_language"`
TargetLanguage string `json:"target_language"`
}
type DictResponse struct {
Details []struct {
Detail string `json:"detail"`
Extra string `json:"extra"`
} `json:"details"`
}
type DictResponseData struct {
Result []struct {
Ce struct {
Basic struct {
Explains []struct {
Text string `json:"text"`
Pos string `json:"pos"`
Trans string `json:"trans"`
} `json:"explains"`
} `json:"basic"`
} `json:"ce"`
} `json:"result"`
}
func main() {
var sourceStr string
fmt.Scanf("%v", &sourceStr)
client := &http.Client{}
request := requestBody{
Source: "youdao",
Words: []string{sourceStr},
SourceLanguage: "zh",
TargetLanguage: "en",
}
buf, err := json.Marshal(request)
if err != nil {
log.Fatal(err)
}
var data = bytes.NewReader(buf)
req, err := http.NewRequest("POST", "https://translate.volcengine.com/web/dict/detail/v1/?msToken=&X-Bogus=DFSzswVLQDGu/0rktHkdsVXAIQ2c&_signature=_02B4Z6wo00001GPw-CAAAIDBAPo4Swy4vuBj8PyAAHxAGHmhE0NX05JYqqJdK-F2Ap7KJ58xWV5eq8eATV4u-z0JqSXiLFQZybDD-aDAJjNfsugGJrCKUNSdhUsuxzCVe.jSB25inFNEKLnub6", data)
if err != nil {
log.Fatal(err)
}
req.Header.Set("authority", "translate.volcengine.com")
req.Header.Set("accept", "application/json, text/plain, */*")
req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en-GB-oxendict;q=0.8,en;q=0.7")
req.Header.Set("content-type", "application/json")
req.Header.Set("cookie", "x-jupiter-uuid=16906005435166114; i18next=zh-CN; s_v_web_id=verify_lknfwwif_pHxg2GVe_OCxI_4sgR_9BL8_D8H2bs3fmW7Z; ttcid=a76d32e7cc444ad19882cdd71373d9db41; referrer_title=API%E6%8E%A5%E5%85%A5%E6%B5%81%E7%A8%8B%E6%A6%82%E8%A7%88--%E6%9C%BA%E5%99%A8%E7%BF%BB%E8%AF%91-%E7%81%AB%E5%B1%B1%E5%BC%95%E6%93%8E; isIntranet=-1; ve_doc_history=4640; __tea_cache_tokens_3569={%22web_id%22:%227261074170329646650%22%2C%22user_unique_id%22:%227261074170329646650%22%2C%22timestamp%22:1690600578906%2C%22_type_%22:%22default%22}; tt_scid=9R.7CAfywmjjrkboy0HjKJbKCusQ3eixBF3FgyTVVRAi1qw236zULAW.m2uueq797a09")
req.Header.Set("origin", "https://translate.volcengine.com")
req.Header.Set("referer", "https://translate.volcengine.com/?category=&home_language=zh&source_language=detect&target_language=en&text=%E7%AC%A8%E8%9B%8B")
req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Google Chrome";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", "same-origin")
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")
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)
}
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
item := dictResponse.Details[0]
jsonStr := item.Detail
var myData DictResponseData
err2 := json.Unmarshal([]byte(jsonStr), &myData)
if err != nil {
log.Fatal(err2)
}
fmt.Println("火山翻译")
for _, item := range myData.Result[0].Ce.Basic.Explains {
fmt.Println(item.Text, item.Pos, item.Trans)
}
}
参考
- 字节内部课:Go 语言上手 - 基础语法
- Go 语言教程 | 菜鸟教程 (runoob.com)