对于测试非空字符串(在Go中),哪种方法是最好的(最常用的)?
if len(mystring) > 0 { }
Or:
if mystring != "" { }
还是别的什么?
对于测试非空字符串(在Go中),哪种方法是最好的(最常用的)?
if len(mystring) > 0 { }
Or:
if mystring != "" { }
还是别的什么?
当前回答
使用下面这样的函数会更简洁,更不容易出错:
func empty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
其他回答
到目前为止,Go编译器在这两种情况下生成相同的代码,所以这是一个品味问题。GCCGo确实会生成不同的代码,但几乎没有人使用它,所以我不担心这个问题。
https://godbolt.org/z/fib1x1
假设空格和所有前导和后面的空格都应该被删除:
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
因为: Len("") //为0 Len(" ") //一个空格为1 Len(" ") //两个空格为2
这将比修整整个字符串的性能更好,因为您只需要检查至少一个存在的非空格字符
// Strempty checks whether string contains only whitespace or not
func Strempty(s string) bool {
if len(s) == 0 {
return true
}
r := []rune(s)
l := len(r)
for l > 0 {
l--
if !unicode.IsSpace(r[l]) {
return false
}
}
return true
}
我认为最好的方法是与空白字符串进行比较
BenchmarkStringCheck1检查空字符串
BenchmarkStringCheck2检查len 0
我检查空字符串和非空字符串检查。您可以看到,使用空字符串进行检查更快。
BenchmarkStringCheck1-4 2000000000 0.29 ns/op 0 B/op 0 allocs/op
BenchmarkStringCheck1-4 2000000000 0.30 ns/op 0 B/op 0 allocs/op
BenchmarkStringCheck2-4 2000000000 0.30 ns/op 0 B/op 0 allocs/op
BenchmarkStringCheck2-4 2000000000 0.31 ns/op 0 B/op 0 allocs/op
Code
func BenchmarkStringCheck1(b *testing.B) {
s := "Hello"
b.ResetTimer()
for n := 0; n < b.N; n++ {
if s == "" {
}
}
}
func BenchmarkStringCheck2(b *testing.B) {
s := "Hello"
b.ResetTimer()
for n := 0; n < b.N; n++ {
if len(s) == 0 {
}
}
}
只是补充更多的评论
主要是关于如何做性能测试。
我用以下代码进行了测试:
import (
"testing"
)
var ss = []string{"Hello", "", "bar", " ", "baz", "ewrqlosakdjhf12934c r39yfashk fjkashkfashds fsdakjh-", "", "123"}
func BenchmarkStringCheckEq(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss {
if s == "" {
c++
}
}
}
t := 2 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
func BenchmarkStringCheckLen(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss {
if len(s) == 0 {
c++
}
}
}
t := 2 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
func BenchmarkStringCheckLenGt(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss {
if len(s) > 0 {
c++
}
}
}
t := 6 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
func BenchmarkStringCheckNe(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss {
if s != "" {
c++
}
}
}
t := 6 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
结果是:
% for a in $(seq 50);do go test -run=^$ -bench=. --benchtime=1s ./...|grep Bench;done | tee -a log
% sort -k 3n log | head -10
BenchmarkStringCheckEq-4 150149937 8.06 ns/op
BenchmarkStringCheckLenGt-4 147926752 8.06 ns/op
BenchmarkStringCheckLenGt-4 148045771 8.06 ns/op
BenchmarkStringCheckNe-4 145506912 8.06 ns/op
BenchmarkStringCheckLen-4 145942450 8.07 ns/op
BenchmarkStringCheckEq-4 146990384 8.08 ns/op
BenchmarkStringCheckLenGt-4 149351529 8.08 ns/op
BenchmarkStringCheckNe-4 148212032 8.08 ns/op
BenchmarkStringCheckEq-4 145122193 8.09 ns/op
BenchmarkStringCheckEq-4 146277885 8.09 ns/op
有效变体通常不会达到最快时间,不同变体的最高速度之间只有最小的差异(约0.01ns/op)。
如果我看完整的日志,try之间的差异大于benchmark函数之间的差异。
而且似乎没有任何可测量的差异之间 BenchmarkStringCheckEq和BenchmarkStringCheckNe 或BenchmarkStringCheckLen和BenchmarkStringCheckLenGt 即使后面的变体应该inc c 6次而不是2次。
通过添加带有修改过的测试或内循环的测试,您可以尝试获得对相同性能的信心。这样更快:
func BenchmarkStringCheckNone4(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, _ = range ss {
c++
}
}
t := len(ss) * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
这并不是更快:
func BenchmarkStringCheckEq3(b *testing.B) {
ss2 := make([]string, len(ss))
prefix := "a"
for i, _ := range ss {
ss2[i] = prefix + ss[i]
}
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss2 {
if s == prefix {
c++
}
}
}
t := 2 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
这两种变体通常比主要测试之间的差异更快或更慢。
使用具有相关分布的字符串生成器生成测试字符串(ss)也很好。长度也是可变的。
所以我对go中测试空字符串的主要方法之间的性能差异没有任何信心。
我可以有信心地说,不测试空字符串比测试空字符串更快。而且测试空字符串比测试一个字符字符串(前缀变体)更快。