有更优雅的方式吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有更优雅的方式吗?相关的知识,希望对你有一定的参考价值。

我写了一个Golang代码示例,它将查询发送到postgres并将结果发送到寻呼机:

package main

import (
    "fmt"
    "database/sql"
    _ "github.com/lib/pq"
    "log"
    "os/exec"
    "strings"
    "os"
)

func main() {
    connstr := "user=postgres dbname=postgres sslmode=disable"
    db, err := sql.Open("postgres", connstr)
    if err != nil { log.Fatal(err) }

    rows, err := db.Query("SELECT schemaname, relname, seq_scan FROM pg_stat_all_tables ORDER BY 1 LIMIT 10")
    if err != nil { log.Fatal(err) }
    defer rows.Close()

    var buf string
    for rows.Next() {
        var s, r string
        var ss int
        if err := rows.Scan(&s, &r, &ss); err != nil { log.Fatal(err) }
        buf = fmt.Sprintf("%s %s %d
", buf + s, r, ss)
    }

    cmd := exec.Command("less")
    cmd.Stdin = strings.NewReader(buf)
    cmd.Stdout = os.Stdout

    err = cmd.Run()
    if err != nil { log.Fatal(err) }
}

但是以下行:

buf = fmt.Sprintf("%s %s %d
", buf + s, r, ss)

对我来说看起来很粗鲁,我不确定这是一个正确的方法。有没有办法以更优雅的方式达到效果?可能有某种缓冲区和io.Readers可能吗?

答案

Go中的字符串是不可变的,每次为变量赋值时,它必须创建一个新字符串并将现有字符串的内容复制到其中。

您可以使用bytes.Buffer而不是string来避免每次迭代重新创建。

package main

import (
    "fmt"
    "database/sql"
    _ "github.com/lib/pq"
    "log"
    "os/exec"
    "strings"
    "os"
    "bytes"
)

func main() {
    connstr := "user=postgres dbname=postgres sslmode=disable"
    db, err := sql.Open("postgres", connstr)
    if err != nil { log.Fatal(err) }

    rows, err := db.Query("SELECT schemaname, relname, seq_scan FROM pg_stat_all_tables ORDER BY 1 LIMIT 10")
    if err != nil { log.Fatal(err) }
    defer rows.Close()

    var buf = new(bytes.Buffer)
    for rows.Next() {
        var s, r string
        var ss int
        if err := rows.Scan(&s, &r, &ss); err != nil { log.Fatal(err) }
        buf.WriteString(fmt.Sprintf("%s %s %d
", s, r, ss))
    }

    cmd := exec.Command("less")
    cmd.Stdin = buf
    cmd.Stdout = os.Stdout

    err = cmd.Run()
    if err != nil { log.Fatal(err) }
}

顺便说一句,字符串生成器添加在Go 1.10 https://godoc.org/strings#Builder

阅读有关字符串连接基准的更多信息:http://herman.asia/efficient-string-concatenation-in-go

另一答案

你现在正在做的唯一问题是连接运算符+是一种非常低效的方法,可以将大量字符串组合成一个更大的字符串。

效率如何?那么here's a benchmark I did测试三种方法:

BenchmarkMultiplyBasic                    300000              4240 ns/op
BenchmarkMultiplyJoinBasic                200000              9942 ns/op
BenchmarkMultiplyConcatenationsBasic       10000            170523 ns/op

最后一个是连接运算符+,与几个替代方案相比,显示出真正悲惨的性能。

在简化的可运行示例中,这是一种更有效的方法:

package main

import(
    "fmt"
    "strconv"
    "strings"
)

type result struct {
    s, r        string
    ss          int
}

func main() {
    a := []result{
        {"twas", "brillig", 1},
        {"and", "the", 2},
        {"slithy", "toves", 3},
    }
    outstrings := make([]string, 0)
    for _, part := range a {
        outstrings = append(outstrings, part.s, part.r, strconv.Itoa(part.ss))
    }
    out := strings.Join(outstrings, ` `)
    fmt.Printf("%s
", out)
}

版画

twas brillig 1 and the 2 slithy toves 3

如何最有效地组合字符串是StackOverflow上的常见问题,并且已经多次回答。看看Go:How to efficiently concatenate strings in Go?最热门的问题/答案。

以上是关于有更优雅的方式吗?的主要内容,如果未能解决你的问题,请参考以下文章

展开可选类型的最佳方法[重复]

是否有更优雅的形式将 NULL 分配给 InsertCommand 的 NVarChar?

更优雅的方式来处理 UIActionSheet 中的取消按钮单击?

这段javascript代码能写得优雅吗?

C++:对于这种(多调度)运行时多态性,是不是有更优雅的解决方案?

Ruby 优雅的方式为版本字符串提供默认数组值