String 在Go中测试空字符串的最佳方法是什么?
对于测试非空字符串(在Go中),哪种方法最好(更具idomatic) 或:String 在Go中测试空字符串的最佳方法是什么?,string,go,is-empty,String,Go,Is Empty,对于测试非空字符串(在Go中),哪种方法最好(更具idomatic) 或: 还是别的什么?这似乎是过早的微观优化。编译器可以自由地为这两种情况生成相同的代码,或者至少为这两种情况生成相同的代码 if len(s) != 0 { ... } 及 因为语义显然是相等的。这两种样式都在Go的标准库中使用 if len(s) > 0 { ... } 可在strconv的软件包中找到: 可以在编码/json包中找到: 这两种语言都是惯用的,而且足够清晰。这更多的是个人品味和清晰度的问题 罗斯·考
还是别的什么?这似乎是过早的微观优化。编译器可以自由地为这两种情况生成相同的代码,或者至少为这两种情况生成相同的代码
if len(s) != 0 { ... }
及
因为语义显然是相等的。这两种样式都在Go的标准库中使用
if len(s) > 0 { ... }
可在strconv的软件包中找到:
可以在编码/json
包中找到:
这两种语言都是惯用的,而且足够清晰。这更多的是个人品味和清晰度的问题
罗斯·考克斯在一篇文章中写道:
使代码清晰的代码。
如果我要看元素x,我通常写
len(s)>x,即使对于x==0,但如果我关心
“就是这个特定的字符串”我倾向于写s==“”
假设一个成熟的编译器将编译
len(s)==0和s==“”转换为相同的有效代码。
把代码弄清楚
正如中所指出的,Go编译器在这两种情况下都会生成相同的代码。检查长度是一个很好的答案,但您也可以解释一个“空”字符串,它也是唯一的空白。“技术上”不是空的,但如果您想检查:
package main
import (
"fmt"
"strings"
)
func main() {
stringOne := "merpflakes"
stringTwo := " "
stringThree := ""
if len(strings.TrimSpace(stringOne)) == 0 {
fmt.Println("String is empty!")
}
if len(strings.TrimSpace(stringTwo)) == 0 {
fmt.Println("String two is empty!")
}
if len(stringTwo) == 0 {
fmt.Println("String two is still empty!")
}
if len(strings.TrimSpace(stringThree)) == 0 {
fmt.Println("String three is empty!")
}
}
假设应删除空格以及所有前导空格和尾随空格:
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
因为:
len(“”//为0
len(“”//一个空格等于1
len(“”//两个空格是2
使用下面这样的函数会更干净,更不容易出错:
func empty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
这将比修剪整个字符串更有效,因为您只需要检查是否存在至少一个非空格字符
// 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
}
到目前为止,Go编译器在这两种情况下生成相同的代码,因此这是一个品味问题。GCCGo确实生成不同的代码,但几乎没有人使用它,所以我不必担心这一点
我认为最好的方法是与空白字符串进行比较
BenchmarkStringCheck1正在使用空字符串进行检查
基准测试Tringcheck2正在与len zero进行检查
我使用空字符串和非空字符串检查进行检查。您可以看到,使用空白字符串进行检查更快
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
代码
func BenchmarkStringCheck1(b*testing.b){
s:=“你好”
b、 重置计时器()
对于n:=0;n
只是为了给
主要是关于如何做性能测试
我使用以下代码进行了测试:
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)
}
}
实际上,变型通常不会达到最快速度,且变型最高速度之间的差异最小(约为0.01ns/op)
若我查看完整日志,尝试之间的差异大于基准函数之间的差异
此外,两者之间似乎没有任何可测量的差异
基准Tringcheckeq和基准Tringcheckene
或基准测试Tringchecklen和基准测试Tringcheckleng
即使后一种变体应该包含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 BenchmarkStringCheckNone4(b*testing.b){
c:=0
b、 重置计时器()
对于n:=0;n
这并不是更快:
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)
}
}
func BenchmarkStringCheckEq3(b*testing.b){
ss2:=制造([]字符串,长度(ss))
前缀:=“a”
对于i,z:=范围ss{
ss2[i]=前缀+ss[i]
}
c:=0
b、 重置计时器()
对于n:=0;n
这两种变体通常比主要测试之间的差异快或慢
使用具有相关分布的字符串生成器生成测试字符串(ss)也很好。而且长度也可变
所以我对在go中测试空字符串的主要方法之间的性能差异没有任何信心
我可以自信地说,完全不测试空字符串比测试空字符串要快。而且,测试空字符串比测试1个字符字符串(前缀变量)更快。根据官方指南,从性能角度来看,它们看起来是等效的(),s!=“”由于句法上的优势,会更好。s!=“”将在编译时失败,如果变量不是字符串,而len==0将传递其他几种数据类型。为什么会有这种假设?那家伙清楚地说出了空字符串。同样,假设字符串中只需要ascii字符,然后添加一个删除所有非ascii字符的函数。我假设他想确保他之前初始化的变量在“技术上”仍然是空的,这正是我在这篇文章中需要的。我需要用户输入至少有一个非空白字符,这一行是明确和简洁的。我所需要做的就是做一个if条件,我不同意这个答案。如果mystring!=“{}
是当今最好的、首选的、惯用的方式。标准库包含其他内容的原因是它是在2010年之前编写的,当时len(mystring)==0
优化是有意义的。@honza
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
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
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)
}
}