Réponses:
Les deux styles sont utilisés dans les bibliothèques standard de Go.
if len(s) > 0 { ... }
peut être trouvé dans le strconv
package: http://golang.org/src/pkg/strconv/atoi.go
if s != "" { ... }
peut être trouvé dans le encoding/json
package: http://golang.org/src/pkg/encoding/json/encode.go
Les deux sont idiomatiques et assez clairs. C'est plus une question de goût personnel et de clarté.
Russ Cox écrit dans un fil de noix de golang :
Celui qui rend le code clair.
Si je suis sur le point de regarder l'élément x, j'écris généralement
len (s)> x, même pour x == 0, mais si je me soucie de
"est-ce cette chaîne spécifique", j'ai tendance à écrire s == "".Il est raisonnable de supposer qu'un compilateur mature compilera
len (s) == 0 et s == "" dans le même code efficace.
...Rendez le code clair.
Comme indiqué dans la réponse de Timmmm , le compilateur Go génère du code identique dans les deux cas.
len
pour vérifier les chaînes vides / non vides. Comme cet engagement de Brad Fitzpatrick. J'ai bien peur que ce soit encore une question de goût et de clarté;)
len(v) > 0
dans h2_bundle.go (ligne 2702). Il ne s'affiche pas automatiquement car il est généré à partir de golang.org/x/net/http2, je crois.
Cela semble être une microoptimisation prématurée. Le compilateur est libre de produire le même code pour les deux cas ou au moins pour ces deux
if len(s) != 0 { ... }
et
if s != "" { ... }
parce que la sémantique est clairement égale.
La vérification de la longueur est une bonne réponse, mais vous pouvez également prendre en compte une chaîne "vide" qui n'est également qu'un espace blanc. Pas "techniquement" vide, mais si vous voulez vérifier:
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
va allouer et copier une nouvelle chaîne à partir de la chaîne d'origine, donc cette approche introduira des inefficacités à grande échelle.
s
est de type chaîne, s[0:i]
retourne une nouvelle copie. Les chaînes sont immuables dans Go, faut-il donc en créer une copie ici?
strings.TrimSpace( s )
n'entraînera pas une nouvelle allocation de chaîne et une nouvelle copie de caractères si la chaîne n'a pas besoin d'être coupée, mais si la chaîne a besoin d'être coupée, la copie supplémentaire (sans les espaces) sera invoquée.
gocritic
linter suggère d'utiliser à la strings.TrimSpace(str) == ""
place du contrôle de longueur.
En supposant que les espaces vides et tous les espaces blancs de début et de fin doivent être supprimés:
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
Car :
len("") // is 0
len(" ") // one empty space is 1
len(" ") // two empty spaces is 2
< 1
+1
À partir de maintenant, le compilateur Go génère du code identique dans les deux cas, c'est donc une question de goût. GCCGo génère un code différent, mais presque personne ne l'utilise, donc je ne m'inquiéterais pas de cela.
Il serait plus propre et moins sujet aux erreurs d'utiliser une fonction comme celle ci-dessous:
func empty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
Juste pour ajouter plus de commentaires
Principalement sur la façon de faire des tests de performances.
J'ai fait des tests avec le code suivant:
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)
}
}
Et les résultats ont é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
En effet, les variantes n'atteignent généralement pas le temps le plus rapide et il n'y a qu'une différence minimale (environ 0,01 ns / op) entre la vitesse de pointe des variantes.
Et si je regarde le journal complet, la différence entre les essais est supérieure à la différence entre les fonctions de référence.
De plus, il ne semble pas y avoir de différence mesurable entre BenchmarkStringCheckEq et BenchmarkStringCheckNe ou BenchmarkStringCheckLen et BenchmarkStringCheckLenGt même si ces dernières variantes doivent augmenter c 6 fois au lieu de 2 fois.
Vous pouvez essayer d'obtenir une certaine confiance sur des performances égales en ajoutant des tests avec un test modifié ou une boucle interne. C'est plus rapide:
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)
}
}
Ce n'est pas plus rapide:
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)
}
}
Les deux variantes sont généralement plus rapides ou plus lentes que la différence entre les tests principaux.
Il serait également bon de générer des chaînes de test (ss) en utilisant un générateur de chaînes avec une distribution appropriée. Et ont également des longueurs variables.
Je n'ai donc aucune confiance en la différence de performances entre les principales méthodes pour tester une chaîne vide en cours.
Et je peux affirmer avec une certaine confiance, il est plus rapide de ne pas tester la chaîne vide du tout que de tester la chaîne vide. Et il est également plus rapide de tester une chaîne vide que de tester 1 chaîne de caractères (variante de préfixe).
Selon les directives officielles et du point de vue des performances, ils semblent équivalents ( réponse ANisus ), le s! = "" Serait préférable en raison d'un avantage syntaxique. s! = "" échouera au moment de la compilation si la variable n'est pas une chaîne, tandis que len (s) == 0 passera pour plusieurs autres types de données.
len()
suffit de ce petit peu de travail supplémentaire. TOUTEFOIS, une chose que nous avions l'habitude de faire en C était de convertir le côté gauche en a const
ou de mettre la chaîne statique du côté gauche de l'opérateur pour empêcher s == "" de devenir s = "" ce qui dans la syntaxe C est acceptable. .. et probablement golang aussi. (voir l'extension si)
Ce serait plus performant que de rogner la chaîne entière, car il vous suffit de vérifier au moins un seul caractère non espace existant
// 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
}
Je pense que la meilleure façon est de comparer avec une chaîne vide
BenchmarkStringCheck1 vérifie avec une chaîne vide
BenchmarkStringCheck2 vérifie avec len zéro
Je vérifie avec la vérification de chaîne vide et non vide. Vous pouvez voir que la vérification avec une chaîne vide est plus rapide.
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 != "" { }
la meilleure façon, préférée et idiomatique AUJOURD'HUI. La raison pour laquelle la bibliothèque standard contient autre chose est qu'elle a été écrite avant 2010 lorsque l'len(mystring) == 0
optimisation était logique.