在这里,我试图从包含字符串的切片中为我的API创建查询字符串。
即。 where={"node_name":"node1","node_name":"node_2"}
import (
"fmt"
"strings"
)
func main() {
nodes := []string{"node1", "node2"}
var query string
for _, n := range nodes {
query += fmt.Sprintf("\"node_name\":\"%s\",", n)
}
query = strings.TrimRight(query, ",")
final := fmt.Sprintf("where={%s}", query)
fmt.Println(final)
}
这是goplayground链接。
获得结果的最佳方法是什么?
由于string
串联,您的解决方案使用了太多分配。
我们将创建一些替代的,更快的和/或更优雅的解决方案。请注意,以下解决方案不检查节点值是否包含引号"
字符。如果愿意,则必须以某种方式对其进行转义(否则结果将是无效的查询字符串)。
完整的可运行代码可以在Go
Playground
上找到。完整的测试/基准测试代码也可以在Go
Playground
上找到,但是它不可运行,将它们保存到Go工作区(例如$GOPATH/src/query/query.go
和$GOPATH/src/query/query_test.go
)中,然后使用运行go test -bench .
。
另外,请务必检查以下相关问题:如何在Go中有效地串联字符串?
您的逻辑可以通过以下功能捕获:
func buildOriginal(nodes []string) string {
var query string
for _, n := range nodes {
query += fmt.Sprintf("\"node_name\":\"%s\",", n)
}
query = strings.TrimRight(query, ",")
return fmt.Sprintf("where={%s}", query)
}
bytes.Buffer
更好的方法是使用单个缓冲区,例如bytes.Buffer
,在其中构建查询,然后将其转换为string
:
func buildBuffer(nodes []string) string {
buf := &bytes.Buffer{}
buf.WriteString("where={")
for i, v := range nodes {
if i > 0 {
buf.WriteByte(',')
}
buf.WriteString(`"node_name":"`)
buf.WriteString(v)
buf.WriteByte('"')
}
buf.WriteByte('}')
return buf.String()
}
使用它:
nodes := []string{"node1", "node2"}
fmt.Println(buildBuffer(nodes))
输出:
where={"node_name":"node1","node_name":"node2"}
bytes.Buffer
改善的bytes.Buffer
尽管比原始解决方案要少得多,但仍会进行一些重新分配。
但是,如果在创建bytes.Buffer
using 时传递了足够大的字节片,则仍可以将分配减少为1
bytes.NewBuffer()
。我们可以事先计算所需的大小:
func buildBuffer2(nodes []string) string {
size := 8 + len(nodes)*15
for _, v := range nodes {
size += len(v)
}
buf := bytes.NewBuffer(make([]byte, 0, size))
buf.WriteString("where={")
for i, v := range nodes {
if i > 0 {
buf.WriteByte(',')
}
buf.WriteString(`"node_name":"`)
buf.WriteString(v)
buf.WriteByte('"')
}
buf.WriteByte('}')
return buf.String()
}
请注意,在size
计算8
是字符串的大小where={}
和15
是字符串的大小"node_name":"",
。
text/template
我们还可以创建一个文本模板,并使用该text/template
程序包执行它,从而有效地生成结果:
var t = template.Must(template.New("").Parse(templ))
func buildTemplate(nodes []string) string {
size := 8 + len(nodes)*15
for _, v := range nodes {
size += len(v)
}
buf := bytes.NewBuffer(make([]byte, 0, size))
if err := t.Execute(buf, nodes); err != nil {
log.Fatal(err) // Handle error
}
return buf.String()
}
const templ = `where={
{{- range $idx, $n := . -}}
{{if ne $idx 0}},{{end}}"node_name":"{{$n}}"
{{- end -}}
}`
strings.Join()
由于其简单性,该解决方案很有趣。我们可以使用适当的前缀和后缀之间strings.Join()
的静态文本来连接节点","node_name":"
。
需要注意的重要事项:strings.Join()
将内置copy()
函数与单个预分配[]byte
缓冲区一起使用,因此非常快!
“作为一种特殊情况,它(该copy()
函数)还将把字节从字符串复制到字节的一部分。”
func buildJoin(nodes []string) string {
if len(nodes) == 0 {
return "where={}"
}
return `where={"node_name":"` + strings.Join(nodes, `","node_name":"`) + `"}`
}
我们将使用以下nodes
值进行基准测试:
var nodes = []string{"n1", "node2", "nodethree", "fourthNode",
"n1", "node2", "nodethree", "fourthNode",
"n1", "node2", "nodethree", "fourthNode",
"n1", "node2", "nodethree", "fourthNode",
"n1", "node2", "nodethree", "fourthNode",
}
基准测试代码如下所示:
func BenchmarkOriginal(b *testing.B) {
for i := 0; i < b.N; i++ {
buildOriginal(nodes)
}
}
func BenchmarkBuffer(b *testing.B) {
for i := 0; i < b.N; i++ {
buildBuffer(nodes)
}
}
// ... All the other benchmarking functions look the same
现在的结果是:
BenchmarkOriginal-4 200000 10572 ns/op
BenchmarkBuffer-4 500000 2914 ns/op
BenchmarkBuffer2-4 1000000 2024 ns/op
BenchmarkBufferTemplate-4 30000 77634 ns/op
BenchmarkJoin-4 2000000 830 ns/op
有些令人吃惊的事实:buildBuffer()
是 3.6倍
的速度比buildOriginal()
,和buildBuffer2()
(同预先计算的大小)约 30%
的速度比buildBuffer()
,因为它并不需要重新分配(并复制)的内部缓冲区。
一些令人惊讶的事实:buildJoin()
非常快,甚至击败buildBuffer2()
由 2.4倍
(因为只使用到[]byte
和copy()
)。buildTemplate()
另一方面证明速度很慢:比慢
7倍buildOriginal()
。这样做的主要原因是因为它在引擎盖下使用(必须使用)反射。
问题内容: 如何获取os.Error的字符串值?即。分配给变量。 问题答案: 例如, 输出:
问题内容: 我有一个字符串,例如: 其中(逗号)将始终是最后一个字符的第三个字符,也就是。 我正在考虑删除’,’的方法,但只能考虑将字符串转换为列表,将其删除,然后将其转换回字符串。但是,对于简单任务而言,这似乎有点过多。 如何以更简单的方式完成此任务? 问题答案: 通常,您只需执行以下操作: 该给你一个字符串,最多,但不包括你要删除的逗号()和给你另一个字符串开始一个字符超出了逗号()。 然后,
问题内容: 我想将字符串切片转换为指向字符串的指针切片 %!p(string = a)=>字符串 %!p(string = b)=>字符串 %!p(string = c)=>字符串 [0xc42000e1d0 0xc42000e1d0 0xc42000e1d0] 据我了解, 我的变量似乎是一个字符串,而不是指向字符串的指针。 因此应从迭代时复制。 显然我不正确,因为地址仍然相同。如果值不是指针,该
问题内容: 我想通过实现以下目标: 我能够做到的唯一方法是: 现在,这个示例以及我真正拥有的是一个长长且混乱的字符串,因此我想将切片放入。我已经研究了docs,但无法找出正确的语法。我的问题是:是否可以在替换字段中切片字符串? 问题答案: 不可以,您无法将切片应用于替换字段中的字符串。 您需要参考Format Specification Mini- Language ;它定义了什么 是 可能的。这
本文向大家介绍详解Python字符串切片,包括了详解Python字符串切片的使用技巧和注意事项,需要的朋友参考一下 在python中,我们定义好一个字符串,如下所示。 在python中定义个字符串然后把它赋值给一个变量。 我们可以通过下标访问单个的字符,跟所有的语言一样,下标从0开始(==,我自己都觉得写的好脑残了) 这个时候呢,我们可以通过切片的方式来截取出我们定义的字符串的一部分。 使用切片的
本文向大家介绍Python中的字符串切片(截取字符串)的详解,包括了Python中的字符串切片(截取字符串)的详解的使用技巧和注意事项,需要的朋友参考一下 字符串索引示意图 字符串切片也就是截取字符串,取子串 Python中字符串切片方法 字符串[开始索引:结束索引:步长] 切取字符串为开始索引到结束索引-1内的字符串 步长不指定时步长为1 字符串[开始索引:结束索引] 练习样例 截取2-末尾的字