【发布时间】:2019-11-09 23:27:20
【问题描述】:
检查 Go 字符串和字节切片是否包含相同字节的最佳方法是什么?最简单的str == string(byteSlice) 效率低,因为它先复制byteSlice。
我正在寻找以字符串作为参数的Equal(a, b []byte) 版本,但找不到合适的。
【问题讨论】:
-
并不是说在 Go 1.5 中“有”strings.Compare,但它的设计效率极低。
标签: go
检查 Go 字符串和字节切片是否包含相同字节的最佳方法是什么?最简单的str == string(byteSlice) 效率低,因为它先复制byteSlice。
我正在寻找以字符串作为参数的Equal(a, b []byte) 版本,但找不到合适的。
【问题讨论】:
标签: go
从 Go 1.5 开始,编译器在使用 stack-allocated temporary 与字符串进行比较时优化字符串(字节)。因此从 Go 1.5 开始
str == string(byteSlice)
成为将字符串与字节切片进行比较的规范且有效的方法。
【讨论】:
len(str) == len(byteSlice) && str == string(byteSlice) - 看起来这是因为 Go 创建了临时切片(没有分配,但仍然需要一点时间)才能比较长度。
The Go Programming Language Specification
字符串类型表示字符串值的集合。字符串值是 (可能为空)字节序列。预先声明的字符串类型是 字符串。
字符串 s 的长度(它的字节大小)可以使用 内置函数 len。可以通过整数访问字符串的字节 索引 0 到 len(s)-1。
例如,
package main
import "fmt"
func equal(s string, b []byte) bool {
if len(s) != len(b) {
return false
}
for i, x := range b {
if x != s[i] {
return false
}
}
return true
}
func main() {
s := "equal"
b := []byte(s)
fmt.Println(equal(s, b))
s = "not" + s
fmt.Println(equal(s, b))
}
输出:
true
false
【讨论】:
如果您对这可能会在以后的版本中中断这一事实感到满意(尽管值得怀疑),您可以使用unsafe:
func unsafeCompare(a string, b []byte) int {
abp := *(*[]byte)(unsafe.Pointer(&a))
return bytes.Compare(abp, b)
}
func unsafeEqual(a string, b []byte) bool {
bbp := *(*string)(unsafe.Pointer(&b))
return a == bbp
}
// using:
// aaa = strings.Repeat("a", 100)
// bbb = []byte(strings.Repeat("a", 99) + "b")
// go 1.5
BenchmarkCopy-8 20000000 75.4 ns/op
BenchmarkPetersEqual-8 20000000 83.1 ns/op
BenchmarkUnsafe-8 100000000 12.2 ns/op
BenchmarkUnsafeEqual-8 200000000 8.94 ns/op
// go 1.4
BenchmarkCopy 10000000 233 ns/op
BenchmarkPetersEqual 20000000 72.3 ns/op
BenchmarkUnsafe 100000000 15.5 ns/op
BenchmarkUnsafeEqual 100000000 10.7 ns/op
【讨论】:
str == string(byteSlice) 对于他的工作量来说效率很低,因为它复制了它,这是可以理解的。
没有理由使用 unsafe 包或其他东西来比较 []byte 和 string。 Go 编译器现在已经足够聪明了,它可以优化这种转换。
BenchmarkEqual-8 172135624 6.96 ns/op <--
BenchmarkUnsafe-8 179866616 6.65 ns/op <--
BenchmarkUnsafeEqual-8 175588575 6.85 ns/op <--
BenchmarkCopy-8 23715144 47.3 ns/op
BenchmarkPetersEqual-8 24709376 47.3 ns/op
只需将字节切片转换为字符串并进行比较:
var (
aaa = strings.Repeat("a", 100)
bbb = []byte(strings.Repeat("a", 99) + "b")
)
func BenchmarkEqual(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = aaa == string(bbb)
}
}
【讨论】: