前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >本周Golang复盘

本周Golang复盘

作者头像
公众号guangcity
发布2021-07-30 10:45:50
3900
发布2021-07-30 10:45:50
举报
文章被收录于专栏:光城(guangcity)光城(guangcity)

本周Golang复盘

1.Http请求

http get请求时,通常会拼接url操作,例如:http://xxx.com?code=1&time=2021-07-01 12:00:00

特别是像时间这种涉及特殊字符-、空格、:等的字段,直接拼接后发起get请求,会导致返回的json数据(或者其他数据)解析失败,或者拿不到数据的情况。

原因在于url需要进行encode编码,这里可采用url.QueryEscape()函数进行编码。

2.defer+slice案例

2.1 示例1

这里以这个示例:

代码语言:javascript
复制
func test() {
 n := 10
 data := make([]string, n, 2*n)
 fmt.Printf("data : %p\n", data)
 defer unProcess(data) // 统一处理
 data[0] = "hello"
}
func unProcess(data []string) {
 fmt.Printf("process data:%p\n", data)
 fmt.Println(data, len(data))
}

这个例子输出结果与我们预期基本一致,对于切片data长度为n,那么修改index=0位置的元素,在unProcess中也可以看得见。

输出结果:

代码语言:javascript
复制
data : 0xc00007e000
process data:0xc00007e000
[hello         ] 10

2.2 示例2

那把上面换成append操作呢?

示例:

代码语言:javascript
复制
func test() {
 n := 10
 data := make([]string, n, 2*n)
 fmt.Printf("data : %p\n", data)
 defer unProcess(data) // 统一处理
 data = append(data, "hello")
 fmt.Printf("append data %p \n", data)
}

此时,我们也想达到第一种效果,可现实是残酷的,最后得到了:

代码语言:javascript
复制
data : 0xc000074000
append data 0xc000074000 
process data:0xc000074000
[         ] 10

可以看到两次输出结果就最后一行不一行,区别在于后面使用了append操作,为啥append操作就拿不到数据了呢?

因此golang函数本质上是值传递,这里传递的是切片,切片内部含有底层数组的指针以及len、cap字段。在进入函数的时候,上面都只是打印的data本身地址,那如果打印&data,会发现是另一个地址,这里拷贝的是地址,函数内部的内容是地址,地址的内容是当前状态的len、cap对应的array,那你在外面append的话,len会发生变化,我内部是不可见的。因此,这种操作是看不到数据的。

要想看到数据,这里提供两种方式:

第一种:函数传递时,传递切片所指向的底层数组的真实地址。

代码语言:javascript
复制
func test() {
 n := 10
 data := make([]string, n, 2*n)
 fmt.Printf("data : %p\n", data)
 defer unProcess(&data) // 统一处理
 data = append(data, "hello")
 fmt.Printf("append data %p \n", data)
}
func unProcess(data *[]string) {
 fmt.Printf("process data:%p\n", data)
 fmt.Println(data, len(*data))
}

此时,便可以得到跟[] 访问一样的效果了。

第二种:进行reslice。

代码语言:javascript
复制
func unProcess(data []string) {
 data = data[:11]
 fmt.Printf("process data:%p\n", data)
 fmt.Println(data, len(data))
}

2.3 示例3

我们对第一个例子进行修改,主要是修改初始的cap,在[]访问前进行append操作,会得到什么结果呢?

代码语言:javascript
复制
func test() {
 n := 10
 data := make([]string, n, n)
 fmt.Printf("data : %p\n", data)
 defer unProcess(data) // 统一处理
 data = append(data, "hello")
 data[0] = "world"
 fmt.Printf("append data %p \n", data)
}
func unProcess(data []string) {
 fmt.Printf("process data:%p\n", data)
 fmt.Println(data, len(data))
}

答案是:

代码语言:javascript
复制
data : 0xc00007a000
append data 0xc00007e000 
process data:0xc00007a000
[         ] 10

额,又空掉了,为啥呢?

根本原因在于,在append时,cap不够了,需要分配,那就会重新分配底层数组,将原来的内容拷贝进去,此时的data所指向的地址已经发生变化,data[0]也是对修改后的数组进行修改,所以最后是啥都没有。

要想有数据,采用示例2的第一种方法即可。

2.4 示例4

既然知道原理是cap不够,那我现在给他分配足够的cap,让其append之后的结果是什么呢?我们来看看不用defer,把append放到unProcess里面会发生什么呢?

代码语言:javascript
复制
func test() 
 n := 10
 data := make([]string, n, 2*n)
 fmt.Printf("data : %p\n", data)
 unProcess(data) // 统一处理
 data[0] = "world"
 fmt.Printf("append data %p \n", data)
 fmt.Printf("test func data=%v, len=%d\n", data, len(data))
}
func unProcess(data []string) {
 fmt.Printf("process data:%p\n", data)
 data = append(data, "hello")
 fmt.Println(data, len(data))
}

此时输出:

代码语言:javascript
复制
data : 0xc000076000
process data:0xc000076000
[          hello] 11
append data 0xc000076000 
test func data=[world         ], len=10

可以看到最后一行只有world,len只有10,我的hello哪去了,为啥len还是10?

一句话解释这里,因为只能看见当前的len,所以hello字符串是在index=10,len=11时的结果,因此我们需要reslice即可(示例2的第二种方法)。

代码语言:javascript
复制
data = data[:11]
fmt.Printf("test func data=%v, len=%d\n", data, len(data))

此时便可以输出正常结果了:

代码语言:javascript
复制
test func data=[world          hello], len=11

3.io输入

在刷题的时候如果用go处理输入,采用同C/C++类似的Scan函数,fmt.Scan函数会报TLE。

这里可以采用bufio库。

封装的函数有下面两个,其实就是两种方式。

代码语言:javascript
复制
func ScanInput(sc *bufio.Scanner) []string {
    sc.Scan()
    return strings.Split(sc.Text(), " ")
}

func ScanInputReader(sc *bufio.Reader) []string {
    line, _ := sc.ReadString('\n')
    x := strings.Split(strings.Trim(line, "\n"), " ")
    return x
}
  • 自定义buffer大小
代码语言:javascript
复制
sc := bufio.NewScanner(os.Stdin)
bf := make([]byte, 1024*1024)
sc.Buffer(bf, len(bf))
// 输入格式为:5 3
sc.Scan()
row := ScanInput(sc)
n, m := strToInt(row[0]), strToInt(row[1])
  • 不管buffer大小
代码语言:javascript
复制
sc := bufio.NewReader(os.Stdin)
row := ScanInputReader(sc)
n, m := strToInt(row[0]), strToInt(row[1])

这两种方法便可以实现输入了,就是写的太多了,还是C++大法刷题比较快。

4.结构体tag

从配置平台中读取一个yaml结构,然后在代码中使用对应结构体的json格式,比较简单的方法便是定义多个tag。

代码语言:javascript
复制
struct Conf {
  ID `yaml:"id" json:"id"`
  Name `yaml:"name" json:"name"`
}

读取配置文件,返回的便是上面结构体Conf,那么发送http的post请求,需要json格式的对象对应的[]byte。

使用一次:

代码语言:javascript
复制
conf = GetConf()
jsons, errs := json.Marshal(conf)

本节完~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 光城 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本周Golang复盘
    • 1.Http请求
      • 2.defer+slice案例
        • 2.1 示例1
        • 2.2 示例2
        • 2.3 示例3
        • 2.4 示例4
      • 3.io输入
        • 4.结构体tag
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档