477 Stimmen

Was ist der beste Weg, um in Go auf einen leeren String zu testen?

Welche Methode ist am besten (am idiomatischsten) für das Testen von nicht leeren Zeichenketten (in Go)?

if len(mystring) > 0 { }

Oder:

if mystring != "" { }

Oder etwas anderes?

18voto

Janis Viksne Punkte 201

Gemäß den offiziellen Richtlinien und aus Leistungssicht scheinen sie äquivalent zu sein (Antwort von ANisus), das s != "" wäre aufgrund eines syntaktischen Vorteils besser. s != "" wird zur Kompilierzeit fehlschlagen, wenn die Variable kein String ist, während len(s) == 0 für verschiedene andere Datentypen erfolgreich sein wird.

6voto

zhangkai Punkte 105

Ich denke == "" ist schneller und lesbarer.

package main 

import(
    "fmt"
)
func main() {
    n := 1
    s:=""
    if len(s)==0{
        n=2
    }
    fmt.Println("%d", n)
}

Wenn dlv debug playground.go mit len(s) und =="" verglichen wird, bekomme ich diese Situation s == ""

    playground.go:6         0x1008d9d20     810b40f9        MOVD 16(R28), R1       
    playground.go:6         0x1008d9d24     e28300d1        SUB $32, RSP, R2       
    playground.go:6         0x1008d9d28     5f0001eb        CMP R1, R2             
    playground.go:6         0x1008d9d2c     09070054        BLS 56(PC)             
    playground.go:6         0x1008d9d30*    fe0f16f8        MOVD.W R30, -160(RSP)  

    playground.go:6         0x1008d9d34     fd831ff8        MOVD R29, -8(RSP)      
    playground.go:6         0x1008d9d38     fd2300d1        SUB $8, RSP, R29       
    playground.go:7         0x1008d9d3c     e00340b2        ORR $1, ZR, R0         

    playground.go:7         0x1008d9d40     e01f00f9        MOVD R0, 56(RSP)       
    playground.go:8         0x1008d9d44     ff7f05a9        STP (ZR, ZR), 80(RSP)  

    playground.go:9         0x1008d9d48     01000014        JMP 1(PC)                        
    playground.go:10        0x1008d9d4c     e0037fb2        ORR $2, ZR, R0         

Situation len(s)==0

    playground.go:6         0x100761d20     810b40f9        MOVD 16(R28), R1       
    playground.go:6         0x100761d24     e2c300d1        SUB $48, RSP, R2       
    playground.go:6         0x100761d28     5f0001eb        CMP R1, R2             
    playground.go:6         0x100761d2c     29070054        BLS 57(PC)             
    playground.go:6         0x100761d30*    fe0f15f8        MOVD.W R30, -176(RSP)  

    playground.go:6         0x100761d34     fd831ff8        MOVD R29, -8(RSP)      
    playground.go:6         0x100761d38     fd2300d1        SUB $8, RSP, R29       
    playground.go:7         0x100761d3c     e00340b2        ORR $1, ZR, R0         

    playground.go:7         0x100761d40     e02300f9        MOVD R0, 64(RSP)       
    playground.go:8         0x100761d44     ff7f06a9        STP (ZR, ZR), 96(RSP)  
    playground.go:9         0x100761d48     ff2700f9        MOVD ZR, 72(RSP)       

    playground.go:9         0x100761d4c     01000014        JMP 1(PC)              
    playground.go:10        0x100761d50     e0037fb2        ORR $2, ZR, R0         
    playground.go:10        0x100761d54     e02300f9        MOVD R0, 64(RSP)       
    playground.go:10        0x100761d58     01000014        JMP 1(PC)      
    playground.go:6         0x104855d2c     09070054        BLS 56(PC)        

Zitat

1voto

Markus Linnala Punkte 129

Just to add more to Kommentar

Hauptsächlich zum Thema Leistungsprüfung.

Ich habe den folgenden Code getestet:

import (
    "testing"
)

var ss = []string{"Hallo", "", "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("Leere Strings nicht erkannt: %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("Leere Strings nicht erkannt: %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("Leere Strings nicht erkannt: %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("Leere Strings nicht erkannt: %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

In der Regel erreichen die Varianten nicht die schnellste Zeit und es gibt nur einen minimalen Unterschied (ca. 0,01 ns/op) zwischen der Höchstgeschwindigkeit der Varianten.

Und wenn ich mir das gesamte Protokoll anschaue, ist der Unterschied zwischen den Versuchen größer als der Unterschied zwischen den Benchmark-Funktionen.

Außerdem scheint es keinen messbaren Unterschied zwischen BenchmarksStringCheckEq und BenchmarkStringCheckNe oder BenchmarkStringCheckLen und BenchmarkStringCheckLenGt zu geben, auch wenn letztere Varianten c 6 Mal anstatt 2 Mal inkrementieren sollten.

Sie können versuchen, durch Hinzufügen von Tests mit modifiziertem Test oder innerer Schleife etwas Vertrauen in die gleiche Leistung zu bekommen. Dies ist 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("Leere Strings nicht erkannt: %d != %d", c, t)
    }
}

Dies ist nicht schneller:

func BenchmarkStringCheckEq3(b *testing.B) {
    ss2 := make([]string, len(ss))
    präfix := "a"
    for i, _ := range ss {
            ss2[i] = präfix + ss[i]
    }
    c := 0
    b.ResetTimer()
    for n := 0; n < b.N; n++ {
            for _, s := range ss2 {
                    if s == präfix {
                            c++
                    }
            }
    }
    t := 2 * b.N
    if c != t {
            b.Fatalf("Leere Strings nicht erkannt: %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 relevanten Verteilungsgenerator zu generieren. Und variable Längen zu haben.

Also habe ich kein Vertrauen in den Leistungsunterschied zwischen den Hauptmethoden zur Überprüfung eines leeren Strings in Go.

Und ich kann mit einiger Sicherheit sagen, dass es schneller ist, keinen leeren String zu testen, als einen leeren String zu testen. Und es ist auch schneller, einen leeren String zu testen als einen 1-Zeichen-String zu testen (Präfixvariante).

0voto

Es wäre sauberer und fehlerunanfälliger, eine Funktion wie die folgende zu verwenden:

func empty(s string) bool {
    return len(strings.TrimSpace(s)) == 0
}

-1voto

Brian Leishman Punkte 7324

Dies wäre performanter als das Trimmen des gesamten Strings, da Sie nur überprüfen müssen, ob mindestens ein einzelnes Nicht-Leerzeichen vorhanden ist

// Strempty überprüft, ob der String nur Leerzeichen enthält oder nicht
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
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X