我想在Go中优化我的stringpad库。到目前为止,我发现用已知字符值(例如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缓冲区内存副本。是否有一种更优雅的方法可以在初始化时使用已知值创建已知大小的缓冲区?
您只能使用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?