Відповіді:
Обидва стилі використовуються в стандартних бібліотеках 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
Обидва ідіоматичні і досить чіткі. Це більше питання особистого смаку та ясності.
Russ Cox пише в голанг-горіхах нитку :
Той, що робить код зрозумілим.
Якщо я збираюся подивитися на елемент x, я, як правило, пишу
len (s)> x, навіть для x == 0, але якщо мені байдуже
"чи це конкретний рядок," я схильний писати s == "".Доцільно припустити, що зрілий компілятор буде компілювати
len (s) == 0 і s == "" в той же ефективний код.
...Зробити код зрозумілим.
Як зазначено у відповіді Тімммм , компілятор 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
рядок типу, 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, навіть якщо останні варіанти повинні включати 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), використовуючи генератор рядків з відповідним розподілом. І мають змінну довжину теж.
Тому я не маю впевненості в різниці між продуктивністю між основними методами для тестування порожнього рядка на ходу.
І я можу констатувати з певною впевненістю, що швидше взагалі не перевіряти порожній рядок, ніж тестувати порожній рядок. А також швидше тестувати порожній рядок, ніж тестувати 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
оптимізація мала сенс.