Jawaban:
Kedua gaya digunakan dalam perpustakaan standar Go.
if len(s) > 0 { ... }
dapat ditemukan dalam strconv
paket: http://golang.org/src/pkg/strconv/atoi.go
if s != "" { ... }
dapat ditemukan dalam encoding/json
paket: http://golang.org/src/pkg/encoding/json/encode.go
Keduanya idiomatis dan cukup jelas. Ini lebih merupakan masalah selera pribadi dan tentang kejelasan.
Russ Cox menulis di utas kacang kacangan :
Yang membuat kodenya jelas.
Jika saya akan melihat elemen x saya biasanya menulis
len (s)> x, bahkan untuk x == 0, tetapi jika saya peduli
"apakah ini string spesifik ini" Saya cenderung menulis s == "".Masuk akal untuk mengasumsikan bahwa kompiler matang akan mengkompilasi
len (s) == 0 dan s == "" ke dalam kode yang sama dan efisien.
...Buat kode menjadi jelas.
Seperti yang ditunjukkan dalam jawaban Timmmm , kompiler Go memang menghasilkan kode yang identik dalam kedua kasus.
len
untuk memeriksa string kosong / tidak kosong. Seperti ini yang dilakukan oleh Brad Fitzpatrick. Saya khawatir ini masih soal selera dan kejelasan;)
len(v) > 0
pada h2_bundle.go (baris 2702). Itu tidak ditampilkan secara otomatis karena dihasilkan dari golang.org/x/net/http2, saya percaya.
Ini tampaknya merupakan optimasi mikro prematur. Kompiler bebas untuk menghasilkan kode yang sama untuk kedua kasus atau setidaknya untuk keduanya
if len(s) != 0 { ... }
dan
if s != "" { ... }
karena semantiknya jelas sama.
Memeriksa panjang adalah jawaban yang baik, tetapi Anda juga bisa menjelaskan string "kosong" yang juga hanya spasi kosong. Bukan "secara teknis" kosong, tetapi jika Anda ingin memeriksa:
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
akan mengalokasikan dan menyalin string baru dari string asli, sehingga pendekatan ini akan memperkenalkan inefisiensi pada skala.
s
adalah tipe string, s[0:i]
mengembalikan salinan baru. String tidak dapat diubah di Go, jadi apakah itu perlu membuat salinan di sini?
strings.TrimSpace( s )
tidak akan menyebabkan alokasi string baru dan karakter copy jika string tidak perlu pemangkasan, tetapi jika string memang perlu pemangkasan maka salinan tambahan (tanpa karakter spasi) akan dipanggil.
gocritic
linter menyarankan menggunakan strings.TrimSpace(str) == ""
bukannya cek panjang.
Dengan asumsi bahwa ruang kosong dan semua spasi putih depan dan belakang harus dihilangkan:
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
Karena:
len("") // is 0
len(" ") // one empty space is 1
len(" ") // two empty spaces is 2
< 1
+1
Sampai sekarang, kompiler Go menghasilkan kode identik dalam kedua kasus, jadi ini adalah masalah selera. GCCGo memang menghasilkan kode yang berbeda, tetapi nyaris tidak ada yang menggunakannya jadi saya tidak akan khawatir tentang itu.
Akan lebih bersih dan lebih rentan kesalahan untuk menggunakan fungsi seperti di bawah ini:
func empty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
Hanya untuk menambahkan komentar
Terutama tentang bagaimana melakukan pengujian kinerja.
Saya melakukan pengujian dengan kode berikut:
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)
}
}
Dan hasilnya adalah:
% 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
Varian yang efektif biasanya tidak mencapai waktu tercepat dan hanya ada perbedaan minimal (sekitar 0,01ns / op) antara varian top speed.
Dan jika saya melihat log lengkap, perbedaan antara percobaan lebih besar daripada perbedaan antara fungsi benchmark.
Juga sepertinya tidak ada perbedaan yang terukur antara BenchmarkStringCheckEq dan BenchmarkStringCheckNe atau BenchmarkStringCheckLen dan BenchmarkStringCheckLenGt bahkan jika varian terakhir harus termasuk 6 kali, bukannya 2 kali.
Anda dapat mencoba untuk mendapatkan kepercayaan diri tentang kinerja yang sama dengan menambahkan tes dengan tes yang dimodifikasi atau loop dalam. Ini lebih cepat:
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)
}
}
Ini tidak lebih cepat:
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)
}
}
Kedua varian biasanya lebih cepat atau lebih lambat daripada perbedaan antara tes utama.
Ini juga baik untuk menghasilkan string uji (ss) menggunakan generator string dengan distribusi yang relevan. Dan memiliki panjang variabel juga.
Jadi saya tidak memiliki keyakinan perbedaan kinerja antara metode utama untuk menguji string kosong.
Dan saya dapat menyatakan dengan percaya diri, lebih cepat untuk tidak menguji string kosong sama sekali daripada menguji string kosong. Dan juga lebih cepat untuk menguji string kosong daripada menguji 1 string char (varian awalan).
Sesuai pedoman resmi dan dari sudut pandang kinerja mereka tampak setara ( jawaban ANisus ), s! = "" Akan lebih baik karena keuntungan sintaksis. s! = "" akan gagal pada waktu kompilasi jika variabelnya bukan string, sementara len (s) == 0 akan lulus untuk beberapa tipe data lainnya.
len()
hanya membutuhkan sedikit kerja ekstra. NAMUN, satu hal yang biasa kita lakukan di C adalah melemparkan sisi kiri ke a const
atau meletakkan string statis di sisi kiri operator untuk mencegah s == "" dari menjadi s = "" yang dalam sintaks C dapat diterima. .. dan mungkin golang juga. (lihat diperpanjang jika)
Ini akan lebih berkinerja daripada memangkas seluruh string, karena Anda hanya perlu memeriksa setidaknya satu karakter non-spasi yang ada
// 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
}
Saya pikir cara terbaik adalah membandingkan dengan string kosong
BenchmarkStringCheck1 memeriksa dengan string kosong
BenchmarkStringCheck2 memeriksa dengan len nol
Saya memeriksa dengan memeriksa string kosong dan tidak kosong. Anda dapat melihat bahwa memeriksa dengan string kosong lebih cepat.
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
Kode
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 != "" { }
adalah cara terbaik, disukai dan idiomatik HARI INI. Alasan pustaka standar berisi sebaliknya adalah karena itu ditulis sebelum 2010 ketikalen(mystring) == 0
optimasi masuk akal.