我可以用make或new在golang中预填充字符串吗?

时间:2018-08-31 19:18:23

标签: string go buffer slice

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

代码段是:

// 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()
}

我相信弦垫越大,t缓冲区的内存副本就越多。有没有更优雅的方法来创建具有已知值的初始化大小的缓冲区?

1 个答案:

答案 0 :(得分: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?