http get请求时,通常会拼接url操作,例如:http://xxx.com?code=1&time=2021-07-01 12:00:00
特别是像时间这种涉及特殊字符-、空格、:等的字段,直接拼接后发起get请求,会导致返回的json数据(或者其他数据)解析失败,或者拿不到数据的情况。
原因在于url需要进行encode编码,这里可采用url.QueryEscape()函数进行编码。
这里以这个示例:
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中也可以看得见。
输出结果:
data : 0xc00007e000
process data:0xc00007e000
[hello ] 10
那把上面换成append操作呢?
示例:
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)
}
此时,我们也想达到第一种效果,可现实是残酷的,最后得到了:
data : 0xc000074000
append data 0xc000074000
process data:0xc000074000
[ ] 10
可以看到两次输出结果就最后一行不一行,区别在于后面使用了append操作,为啥append操作就拿不到数据了呢?
因此golang函数本质上是值传递,这里传递的是切片,切片内部含有底层数组的指针以及len、cap字段。在进入函数的时候,上面都只是打印的data本身地址,那如果打印&data
,会发现是另一个地址,这里拷贝的是地址,函数内部的内容是地址,地址的内容是当前状态的len、cap对应的array,那你在外面append的话,len会发生变化,我内部是不可见的。因此,这种操作是看不到数据的。
要想看到数据,这里提供两种方式:
第一种:函数传递时,传递切片所指向的底层数组的真实地址。
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。
func unProcess(data []string) {
data = data[:11]
fmt.Printf("process data:%p\n", data)
fmt.Println(data, len(data))
}
我们对第一个例子进行修改,主要是修改初始的cap,在[]
访问前进行append操作,会得到什么结果呢?
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))
}
答案是:
data : 0xc00007a000
append data 0xc00007e000
process data:0xc00007a000
[ ] 10
额,又空掉了,为啥呢?
根本原因在于,在append时,cap不够了,需要分配,那就会重新分配底层数组,将原来的内容拷贝进去,此时的data所指向的地址已经发生变化,data[0]也是对修改后的数组进行修改,所以最后是啥都没有。
要想有数据,采用示例2的第一种方法即可。
既然知道原理是cap不够,那我现在给他分配足够的cap,让其append之后的结果是什么呢?我们来看看不用defer,把append放到unProcess里面会发生什么呢?
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))
}
此时输出:
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的第二种方法)。
data = data[:11]
fmt.Printf("test func data=%v, len=%d\n", data, len(data))
此时便可以输出正常结果了:
test func data=[world hello], len=11
在刷题的时候如果用go处理输入,采用同C/C++类似的Scan函数,fmt.Scan函数会报TLE。
这里可以采用bufio库。
封装的函数有下面两个,其实就是两种方式。
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
}
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])
sc := bufio.NewReader(os.Stdin)
row := ScanInputReader(sc)
n, m := strToInt(row[0]), strToInt(row[1])
这两种方法便可以实现输入了,就是写的太多了,还是C++大法刷题比较快。
从配置平台中读取一个yaml结构,然后在代码中使用对应结构体的json格式,比较简单的方法便是定义多个tag。
struct Conf {
ID `yaml:"id" json:"id"`
Name `yaml:"name" json:"name"`
}
读取配置文件,返回的便是上面结构体Conf,那么发送http的post请求,需要json格式的对象对应的[]byte。
使用一次:
conf = GetConf()
jsons, errs := json.Marshal(conf)
本节完~