Ответы:
Оба стиля используются в стандартных библиотеках Go.
if len(s) > 0 { ... }
можно найти в strconv
пакете: http://golang.org/src/pkg/strconv/atoi.go
if s != "" { ... }
можно найти в encoding/json
пакете: http://golang.org/src/pkg/encoding/json/encode.go
Оба идиоматичны и достаточно понятны. Это больше вопрос личного вкуса и ясности.
Расс Кокс пишет в теме голанг-орехов :
Тот, который делает код понятным.
Если я собираюсь взглянуть на элемент x, я обычно пишу
len (s)> x, даже для x == 0, но если меня волнует
"это конкретная строка", я склонен писать s == "".Разумно предположить, что зрелый компилятор скомпилирует
len (s) == 0 и s == "" в один и тот же эффективный код.
...Сделайте код понятным.
Как указано в ответе Timmmm , компилятор Go генерирует идентичный код в обоих случаях.
len(v) > 0
в h2_bundle.go (строка 2702). Я полагаю, что он не отображается автоматически, поскольку он генерируется с golang.org/x/net/http2.
Это кажется преждевременной микрооптимизацией. Компилятор может создавать один и тот же код для обоих случаев или, по крайней мере, для этих двух
if len(s) != 0 { ... }
и
if s != "" { ... }
потому что семантика явно равна.
Проверка длины - хороший ответ, но вы также можете учесть «пустую» строку, которая также является только пробелом. Не "технически" пусто, но если вы хотите проверить:
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
выделит и скопирует новую строку из исходной строки, поэтому этот подход привнесет неэффективность в масштабе.
s
тип имеет тип string и s[0:i]
возвращает новую копию. Строки являются неизменяемыми в Go, поэтому нужно ли здесь создавать копию?
strings.TrimSpace( s )
не приведет к выделению новой строки и копированию символов, если строка не нуждается в обрезке, но если строка требует обрезки, будет вызвана дополнительная копия (без пробельных символов).
gocritic
ЛИНТЕР предлагает использовать strings.TrimSpace(str) == ""
вместо проверки длины.
Предполагая, что пустые места и все начальные и конечные пробелы должны быть удалены:
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
Так как :
len("") // is 0
len(" ") // one empty space is 1
len(" ") // two empty spaces is 2
< 1
+1
На данный момент компилятор Go генерирует идентичный код в обоих случаях, так что это дело вкуса. GCCGo генерирует другой код, но почти никто не использует его, поэтому я бы не беспокоился об этом.
Было бы чище и менее подвержено ошибкам использовать функцию, подобную приведенной ниже:
func empty(s string) bool {
return len(strings.TrimSpace(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
Эффективно варианты обычно не достигают самого быстрого времени, и есть только минимальная разница (около 0,01 нс / оп) между максимальной скоростью варианта.
И если я смотрю полный журнал, разница между попытками больше, чем разница между контрольными функциями.
Также, по-видимому, между BenchmarkStringCheckEq и BenchmarkStringCheckNe или BenchmarkStringCheckLen и BenchmarkStringCheckLenGt и BenchmarkStringCheckLenGt не существует какой-либо ощутимой разницы, даже если последние варианты должны выполняться c 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 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)
}
}
Оба варианта обычно быстрее или медленнее, чем разница между основными тестами.
Также было бы хорошо генерировать тестовые строки (ss), используя генератор строк с соответствующим распределением. И иметь переменную длину тоже.
Так что я не уверен в разнице в производительности между основными методами для тестирования пустой строки в go.
И я могу с уверенностью сказать, что быстрее вообще не тестировать пустую строку, чем тестировать пустую строку. А также быстрее протестировать пустую строку, чем 1 строку символов (префиксный вариант).
Согласно официальным правилам и с точки зрения производительности они выглядят эквивалентно ( ответ ANisus ), s! = "" Будет лучше из-за синтаксического преимущества. s! = "" потерпит неудачу во время компиляции, если переменная не является строкой, в то время как len (s) == 0 пройдет для нескольких других типов данных.
len()
требуется лишь немного больше работы. ОДНАКО, одна вещь, которую мы делали в C, была приведена с левой стороны к a const
или помещена статическая строка в левую часть оператора, чтобы s == "" не стал s = "", что в синтаксисе C приемлемо. .. и, вероятно, тоже Голанг. (см. расширенный вариант)
Это будет более производительным, чем обрезка всей строки, так как вам нужно проверить только один существующий непробельный символ
// 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
}
Я думаю, что лучше всего сравнить с пустой строкой
BenchmarkStringCheck1 проверяется с пустой строкой
BenchmarkStringCheck2 проверяет с нулем len
Я проверяю с пустой и непустой строкой проверки. Вы можете видеть, что проверка с пустой строкой происходит быстрее.
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 {
}
}
}
if mystring != "" { }
это лучший, предпочтительный и идиоматический способ СЕГОДНЯ. Причина, по которой стандартная библиотека содержит иное, заключается в том, что она была написана до 2010 года, когдаlen(mystring) == 0
оптимизация имела смысл.