Antworten:
Beide Stile werden in den Standardbibliotheken von Go verwendet.
if len(s) > 0 { ... }
finden Sie im strconv
Paket: http://golang.org/src/pkg/strconv/atoi.go
if s != "" { ... }
finden Sie im encoding/json
Paket: http://golang.org/src/pkg/encoding/json/encode.go
Beide sind idiomatisch und klar genug. Es geht eher um den persönlichen Geschmack und um Klarheit.
Russ Cox schreibt in einem Golang-Nuss-Thread :
Derjenige, der den Code klar macht.
Wenn ich mir Element x anschaue, schreibe ich normalerweise
len (s)> x, sogar für x == 0, aber wenn mir
"ist es diese spezielle Zeichenfolge" wichtig ist, schreibe ich eher s == "".Es ist anzunehmen, dass ein ausgereifter Compiler
len (s) == 0 und s == "" in denselben effizienten Code kompiliert.
...Machen Sie den Code klar.
Wie in der Antwort von Timmmm ausgeführt , generiert der Go-Compiler in beiden Fällen identischen Code.
len
, um leere / nicht leere Zeichenfolgen zu überprüfen. So ein Commit von Brad Fitzpatrick. Ich fürchte, es ist immer noch eine Frage des Geschmacks und der Klarheit;)
len(v) > 0
in h2_bundle.go (Zeile 2702). Es wird nicht automatisch angezeigt, da es aus golang.org/x/net/http2 generiert wird, glaube ich.
Dies scheint eine vorzeitige Mikrooptimierung zu sein. Dem Compiler steht es frei, für beide Fälle oder zumindest für diese beiden Fälle denselben Code zu erstellen
if len(s) != 0 { ... }
und
if s != "" { ... }
weil die Semantik eindeutig gleich ist.
Das Überprüfen der Länge ist eine gute Antwort, aber Sie können auch eine "leere" Zeichenfolge berücksichtigen, die ebenfalls nur aus Leerzeichen besteht. Nicht "technisch" leer, aber wenn Sie überprüfen möchten:
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!")
}
}
TrimSpace
wird eine neue Zeichenfolge aus der ursprünglichen Zeichenfolge zuweisen und kopieren, sodass dieser Ansatz zu Ineffizienzen bei der Skalierung führt.
s
Typ s[0:i]
eine neue Kopie zurückgibt. Strings sind in Go unveränderlich. Muss hier also eine Kopie erstellt werden?
strings.TrimSpace( s )
keine neue Zeichenfolgenzuweisung und Zeichenkopie, wenn die Zeichenfolge nicht gekürzt werden muss. Wenn die Zeichenfolge jedoch gekürzt werden muss, wird die zusätzliche Kopie (ohne Leerzeichen) aufgerufen.
gocritic
Linter schlägt vor, strings.TrimSpace(str) == ""
anstelle der Längenprüfung zu verwenden.
Angenommen, leere Leerzeichen und alle führenden und nachfolgenden Leerzeichen sollten entfernt werden:
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
Weil :
len("") // is 0
len(" ") // one empty space is 1
len(" ") // two empty spaces is 2
< 1
+1
Ab sofort generiert der Go-Compiler in beiden Fällen identischen Code, daher ist es Geschmackssache. GCCGo generiert zwar anderen Code, aber kaum jemand verwendet ihn, sodass ich mir darüber keine Sorgen machen würde.
Es wäre sauberer und weniger fehleranfällig, eine Funktion wie die folgende zu verwenden:
func empty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
Nur um mehr zum Kommentar hinzuzufügen
Hauptsächlich darüber, wie Leistungstests durchgeführt werden.
Ich habe mit folgendem Code getestet:
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)
}
}
Und die Ergebnisse waren:
% 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
Effektiv erreichen Varianten normalerweise nicht die schnellste Zeit und es gibt nur einen minimalen Unterschied (ca. 0,01 ns / op) zwischen der Höchstgeschwindigkeit der Variante.
Und wenn ich das vollständige Protokoll betrachte, ist der Unterschied zwischen Versuchen größer als der Unterschied zwischen Benchmark-Funktionen.
Es scheint auch keinen messbaren Unterschied zwischen BenchmarkStringCheckEq und BenchmarkStringCheckNe oder BenchmarkStringCheckLen und BenchmarkStringCheckLenGt zu geben, selbst wenn letztere Varianten 6-mal statt 2-mal enthalten sollten.
Sie können versuchen, ein gewisses Vertrauen in die gleiche Leistung zu gewinnen, indem Sie Tests mit modifiziertem Test oder innerer Schleife hinzufügen. Das geht schneller:
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)
}
}
Das geht nicht schneller:
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)
}
}
Beide Varianten sind normalerweise schneller oder langsamer als der Unterschied zwischen den Haupttests.
Es wäre auch gut, Teststrings (ss) mit einem Stringgenerator mit relevanter Verteilung zu generieren. Und haben auch variable Längen.
Ich habe also kein Vertrauen in den Leistungsunterschied zwischen den Hauptmethoden zum Testen leerer Zeichenfolgen in go.
Und ich kann mit einiger Sicherheit sagen, dass es schneller ist, leere Zeichenfolgen überhaupt nicht zu testen, als leere Zeichenfolgen zu testen. Außerdem ist es schneller, leere Zeichenfolgen zu testen, als 1 Zeichenfolge (Präfixvariante) zu testen.
Gemäß den offiziellen Richtlinien und aus Sicht der Leistung erscheinen sie gleichwertig ( ANisus-Antwort ), das s! = "" Wäre aufgrund eines syntaktischen Vorteils besser. s! = "" schlägt beim Kompilieren fehl, wenn die Variable keine Zeichenfolge ist, während len (s) == 0 für mehrere andere Datentypen übergeben wird.
len()
genau verstanden hat ... trotz aller Optimierungen auf der Welt ist nur ein wenig mehr Arbeit erforderlich. In C haben wir jedoch die linke Seite in a umgewandelt const
oder die statische Zeichenfolge auf die linke Seite des Operators gesetzt, um zu verhindern, dass s == "" zu s = "" wird, was in der C-Syntax akzeptabel ist. .. und wahrscheinlich auch Golang. (siehe das erweiterte if)
Dies wäre leistungsfähiger als das Trimmen der gesamten Zeichenfolge, da Sie nur nach mindestens einem einzelnen Nicht-Leerzeichen suchen müssen
// 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
}
Ich denke, der beste Weg ist, mit leeren Zeichenfolgen zu vergleichen
BenchmarkStringCheck1 prüft mit einer leeren Zeichenfolge
BenchmarkStringCheck2 prüft mit len zero
Ich überprüfe mit der leeren und nicht leeren Zeichenkettenprüfung. Sie können sehen, dass das Überprüfen mit einer leeren Zeichenfolge schneller ist.
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 {
}
}
}
if mystring != "" { }
ist der beste, bevorzugte und idiomatische Weg HEUTE. Der Grund, warum die Standardbibliothek etwas anderes enthält, ist, dass sie vor 2010 geschrieben wurde, als dielen(mystring) == 0
Optimierung sinnvoll war.