我可以用 make 或 new 在 golang 中制作一个预填充的字符串吗?

Posted

技术标签:

【中文标题】我可以用 make 或 new 在 golang 中制作一个预填充的字符串吗?【英文标题】:Can I make a prefilled string in golang with make or new? 【发布时间】:2018-08-31 19:18:23 【问题描述】:

我正在尝试在 Go 中优化我的 stringpad 库。到目前为止,我发现用已知字符值(例如 0 或“”)填充字符串(实际上是 bytes.Buffer)的唯一方法是使用 for 循环。

代码的sn-p是:

// PadLeft pads string on left side with p, c times
func PadLeft(s string, p string, c int) string 
    var t bytes.Buffer
    if c <= 0 
        return s
    
    if len(p) < 1 
        return s
    
    for i := 0; i < c; i++ 
        t.WriteString(p)
    
    t.WriteString(s)
    return t.String()

string pad 越大,我相信 t 缓冲区的内存副本就越多。有没有更优雅的方法在初始化时使用已知值制作已知大小的缓冲区?

【问题讨论】:

【参考方案1】:

您只能使用make()new() 来分配归零的缓冲区(字节切片或数组)。您可以使用composite literals 获取最初包含非零值的切片或数组,但您不能动态描述初始值(索引必须是常量)。

从类似但非常高效的strings.Repeat() 函数中获取灵感。它以给定的计数重复给定的字符串:

func Repeat(s string, count int) string 
    // Since we cannot return an error on overflow,
    // we should panic if the repeat will generate
    // an overflow.
    // See Issue golang.org/issue/16237
    if count < 0 
        panic("strings: negative Repeat count")
     else if count > 0 && len(s)*count/count != len(s) 
        panic("strings: Repeat count causes overflow")
    

    b := make([]byte, len(s)*count)
    bp := copy(b, s)
    for bp < len(b) 
        copy(b[bp:], b[:bp])
        bp *= 2
    
    return string(b)

strings.Repeat() 进行一次分配以获得一个工作缓冲区(这将是一个字节切片[]byte),并使用内置的copy() 函数来复制可重复的字符串。值得注意的一件事是它使用工作副本并尝试以增量方式复制整个副本,这意味着例如如果字符串已被复制 4 次,则复制此缓冲区将使其复制 8 次,依此类推。这将最大限度地减少对 copy() 的调用。该解决方案还利用copy() 可以从string 复制字节而无需将其转换为字节切片的优势。

我们想要的是类似的东西,但我们希望将结果附加到字符串中。

我们可以解决这个问题,只需分配一个在 Repeat() 内使用的缓冲区加上我们要左填充的字符串的长度。

结果(不检查count参数):

func PadLeft(s, p string, count int) string 
    ret := make([]byte, len(p)*count+len(s))

    b := ret[:len(p)*count]
    bp := copy(b, p)
    for bp < len(b) 
        copy(b[bp:], b[:bp])
        bp *= 2
    
    copy(ret[len(b):], s)
    return string(ret)

测试它:

fmt.Println(PadLeft("aa", "x", 1))
fmt.Println(PadLeft("aa", "x", 2))
fmt.Println(PadLeft("abc", "xy", 3))

输出(在Go Playground上试试):

xaa
xxaa
xyxyxyabc

查看类似/相关问题:Is there analog of memset in go?

【讨论】:

谢谢。这就是我一直在寻找的优雅,我知道它就在那里,但我只是没有看到它。这极大地提高了代码的性能一个数量级。在我的基准测试中填充 200 个字符时,它从 3785 ns/op 变为 220 ns/op。当填充 4 个字符时,它从 196 ns/op 变为 73.6 ns/op。这对于提高我的库的性能非常有帮助。

以上是关于我可以用 make 或 new 在 golang 中制作一个预填充的字符串吗?的主要内容,如果未能解决你的问题,请参考以下文章

Golang | new和make方法的区别

Golang new 和 make 的区别

golang new make 区别

golang的内存模型与new()与make()

golang:make和new的区别

go语言的new和make