Respostas:
Ambos os estilos são usados nas bibliotecas padrão do Go.
if len(s) > 0 { ... }
pode ser encontrado no strconv
pacote: http://golang.org/src/pkg/strconv/atoi.go
if s != "" { ... }
pode ser encontrado no encoding/json
pacote: http://golang.org/src/pkg/encoding/json/encode.go
Ambos são idiomáticos e são claros o suficiente. É mais uma questão de gosto pessoal e clareza.
Russ Cox escreve em um tópico de golang-nuts :
Aquele que torna o código claro.
Se estou prestes a olhar para o elemento x, normalmente escrevo
len (s)> x, mesmo para x == 0, mas se me preocupo com
"é essa sequência específica", tendem a escrever s == "".É razoável supor que um compilador maduro compile
len (s) == 0 es == "" no mesmo código eficiente.
...Faça o código claro.
Como apontado na resposta de Timmmm , o compilador Go gera código idêntico nos dois casos.
len(v) > 0
no h2_bundle.go (linha 2702). Não é mostrado automaticamente, pois é gerado a partir de golang.org/x/net/http2, acredito.
Parece microoptimização prematura. O compilador é livre para produzir o mesmo código para os dois casos ou pelo menos para esses dois
if len(s) != 0 { ... }
e
if s != "" { ... }
porque a semântica é claramente igual.
A verificação do comprimento é uma boa resposta, mas você também pode ter uma string "vazia" que também é apenas espaço em branco. Não "tecnicamente" vazio, mas se você deseja verificar:
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
alocará e copiará uma nova string da string original; portanto, essa abordagem introduzirá ineficiências em escala.
s
tipo string, s[0:i]
retornar uma nova cópia. As strings são imutáveis no Go, então é necessário criar uma cópia aqui?
strings.TrimSpace( s )
não causará nova alocação de string e cópia de caracteres se a string não precisar de aparar, mas se a string precisar de aparar, a cópia extra (sem caracteres de espaço em branco) será chamada.
gocritic
linter sugere usar em strings.TrimSpace(str) == ""
vez da verificação de comprimento.
Supondo que os espaços vazios e todos os espaços em branco iniciais e finais devem ser removidos:
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
Porque :
len("") // is 0
len(" ") // one empty space is 1
len(" ") // two empty spaces is 2
< 1
+1
A partir de agora, o compilador Go gera código idêntico nos dois casos, portanto é uma questão de gosto. O GCCGo gera código diferente, mas quase ninguém o usa, então eu não me preocuparia com isso.
Seria mais limpo e menos propenso a erros usar uma função como a abaixo:
func empty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
Apenas para adicionar mais para comentar
Principalmente sobre como fazer testes de desempenho.
Eu testei com o seguinte código:
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)
}
}
E os resultados foram:
% 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
As variantes efetivamente geralmente não atingem o tempo mais rápido e há apenas uma diferença mínima (cerca de 0,01ns / op) entre a velocidade máxima da variante.
E se eu olhar o log completo, a diferença entre as tentativas é maior que a diferença entre as funções de benchmark.
Também não parece haver nenhuma diferença mensurável entre BenchmarkStringCheckEq e BenchmarkStringCheckNe ou BenchmarkStringCheckLen e BenchmarkStringCheckLenGt, mesmo que as últimas variantes devam aumentar c 6 vezes em vez de 2 vezes.
Você pode tentar obter alguma confiança sobre desempenho igual adicionando testes com teste modificado ou loop interno. Isso é mais rápido:
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)
}
}
Isso não é mais rápido:
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)
}
}
Ambas as variantes são geralmente mais rápidas ou mais lentas que a diferença entre os testes principais.
Também seria bom gerar strings de teste (ss) usando gerador de strings com distribuição relevante. E tem comprimentos variáveis também.
Portanto, não tenho nenhuma confiança na diferença de desempenho entre os métodos principais para testar a string vazia em movimento.
E posso afirmar com alguma confiança: é mais rápido não testar a string vazia do que testar a string vazia. E também é mais rápido testar uma string vazia do que testar uma string de char (variante de prefixo).
De acordo com as diretrizes oficiais e do ponto de vista do desempenho, elas parecem equivalentes ( resposta do ANisus ), o s! = "" Seria melhor devido a uma vantagem sintática. s! = "" falhará no tempo de compilação se a variável não for uma string, enquanto len (s) == 0 passará por vários outros tipos de dados.
len()
preciso apenas um pouco de trabalho extra. No entanto, uma coisa que costumávamos fazer em C era converter o lado esquerdo em a const
ou colocar a string estática no lado esquerdo do operador para impedir que s == "" se tornasse s = "", o que na sintaxe C é aceitável. .. e provavelmente golang também. (veja o estendido se)
Isso seria mais eficiente do que aparar toda a cadeia, pois você só precisa verificar se há pelo menos um único caractere não espacial
// 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
}
Eu acho que a melhor maneira é comparar com a string em branco
BenchmarkStringCheck1 está verificando com uma string em branco
BenchmarkStringCheck2 está verificando com len zero
Verifico com a verificação de cadeia vazia e não vazia. Você pode ver que a verificação com uma sequência em branco é mais rápida.
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
Código
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 != "" { }
é a melhor, preferida e idiomática maneira HOJE. A razão pela qual a biblioteca padrão contém o contrário é porque ela foi escrita antes de 2010 quando alen(mystring) == 0
otimização fazia sentido.