Câu trả lời:
Cả hai phong cách đều được sử dụng trong các thư viện tiêu chuẩn của Go.
if len(s) > 0 { ... }
có thể được tìm thấy trong strconv
gói: http://golang.org/src/pkg/strconv/atoi.go
if s != "" { ... }
có thể được tìm thấy trong encoding/json
gói: http://golang.org/src/pkg/encoding/json/encode.go
Cả hai đều là thành ngữ và đủ rõ ràng. Đó là nhiều hơn một vấn đề của sở thích cá nhân và về sự rõ ràng.
Russ Cox viết trong một chủ đề golang-nut :
Một trong đó làm cho mã rõ ràng.
Nếu tôi sắp xem xét phần tử x, tôi thường viết
len (s)> x, ngay cả đối với x == 0, nhưng nếu tôi quan tâm đến
"thì đây có phải là chuỗi cụ thể" Tôi có xu hướng viết s == "".Thật hợp lý khi giả định rằng một trình biên dịch trưởng thành sẽ biên dịch
len (s) == 0 và s == "" thành cùng một mã hiệu quả.
...Làm cho mã rõ ràng.
Như đã chỉ ra trong câu trả lời của Timmmm , trình biên dịch Go sẽ tạo mã giống hệt nhau trong cả hai trường hợp.
len(v) > 0
trong h2_bundle.go (dòng 2702). Nó không tự động được hiển thị vì nó được tạo ra từ golang.org/x/net/http2, tôi tin.
Điều này dường như là vi mô sớm. Trình biên dịch có thể tự do tạo ra cùng một mã cho cả hai trường hợp hoặc ít nhất là cho hai trường hợp này
if len(s) != 0 { ... }
và
if s != "" { ... }
bởi vì ngữ nghĩa rõ ràng là bằng nhau
Kiểm tra độ dài là một câu trả lời tốt, nhưng bạn cũng có thể tính đến một chuỗi "trống" cũng chỉ là khoảng trắng. Không "kỹ thuật" trống, nhưng nếu bạn quan tâm để kiểm tra:
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ẽ phân bổ và sao chép một chuỗi mới từ chuỗi ban đầu, vì vậy phương pháp này sẽ đưa ra sự không hiệu quả ở quy mô.
s
là kiểu chuỗi, s[0:i]
trả về một bản sao mới. Chuỗi là bất biến trong Go, vì vậy nó có cần tạo một bản sao ở đây không?
strings.TrimSpace( s )
sẽ không gây ra sự phân bổ chuỗi và sao chép ký tự mới nếu chuỗi không cần cắt xén, nhưng nếu chuỗi cần cắt xén thì bản sao bổ sung (không có ký tự khoảng trắng) sẽ được gọi.
gocritic
dối đề nghị sử dụng strings.TrimSpace(str) == ""
thay vì kiểm tra độ dài.
Giả sử rằng các khoảng trắng trống và tất cả các khoảng trắng ở đầu và cuối phải được loại bỏ:
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
Bởi vì :
len("") // is 0
len(" ") // one empty space is 1
len(" ") // two empty spaces is 2
< 1
+1
Cho đến nay, trình biên dịch Go tạo mã giống hệt nhau trong cả hai trường hợp, vì vậy đây là vấn đề của hương vị. GCCGo không tạo mã khác nhau, nhưng hầu như không ai sử dụng nó nên tôi sẽ không lo lắng về điều đó.
Nó sẽ sạch hơn và ít bị lỗi hơn khi sử dụng một chức năng như dưới đây:
func empty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
Chỉ cần thêm nhiều hơn để bình luận
Chủ yếu là về cách làm kiểm tra hiệu suất.
Tôi đã thử nghiệm với mã sau đây:
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)
}
}
Và kết quả là:
% 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
Các biến thể hiệu quả thường không đạt được thời gian nhanh nhất và chỉ có sự khác biệt tối thiểu (khoảng 0,01ns / op) giữa tốc độ tối đa của biến thể.
Và nếu tôi xem nhật ký đầy đủ, sự khác biệt giữa các lần thử lớn hơn sự khác biệt giữa các hàm điểm chuẩn.
Ngoài ra, dường như không có bất kỳ sự khác biệt có thể đo lường nào giữa BenchmarkStringCheckEq và BenchmarkStringCheckNe hoặc BenchmarkStringCheckLen và BenchmarkStringCheckLenGt ngay cả khi các biến thể sau nên thay đổi 6 lần thay vì 2 lần.
Bạn có thể cố gắng để có được sự tự tin về hiệu suất tương đương bằng cách thêm các bài kiểm tra với bài kiểm tra sửa đổi hoặc vòng lặp bên trong. Cái này nhanh hơn:
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)
}
}
Đây không phải là nhanh hơn:
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)
}
}
Cả hai biến thể thường nhanh hơn hoặc chậm hơn so với sự khác biệt giữa các thử nghiệm chính.
Cũng có thể tạo chuỗi thử nghiệm (ss) bằng cách sử dụng trình tạo chuỗi với phân phối có liên quan. Và có độ dài thay đổi quá.
Vì vậy, tôi không có bất kỳ sự tự tin nào về sự khác biệt hiệu năng giữa các phương thức chính để kiểm tra chuỗi rỗng.
Và tôi có thể tự tin khẳng định, sẽ nhanh hơn khi không kiểm tra chuỗi trống nào hơn là kiểm tra chuỗi trống. Và cũng nhanh hơn để kiểm tra chuỗi rỗng so với kiểm tra 1 chuỗi char (biến thể tiền tố).
Theo hướng dẫn chính thức và theo quan điểm hiệu suất, chúng có vẻ tương đương ( câu trả lời của ANisus ), s! = "" Sẽ tốt hơn do lợi thế cú pháp. s! = "" sẽ thất bại tại thời gian biên dịch nếu biến không phải là một chuỗi, trong khi len (s) == 0 sẽ truyền cho một số loại dữ liệu khác.
len()
chỉ cần thêm một chút công việc. TUY NHIÊN, một điều chúng ta thường làm trong C đã chuyển bên trái sang a const
hoặc đặt chuỗi tĩnh ở bên trái của toán tử để ngăn s == "" trở thành s = "" trong cú pháp C có thể chấp nhận được. .. và có lẽ golang cũng vậy. (xem phần mở rộng nếu)
Điều này sẽ hiệu quả hơn so với cắt xén toàn bộ chuỗi, vì bạn chỉ cần kiểm tra ít nhất một ký tự không phải không gian duy nhất hiện 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
}
Tôi nghĩ cách tốt nhất là so sánh với chuỗi trống
BenchmarkStringCheck1 đang kiểm tra với chuỗi trống
BenchmarkStringCheck2 đang kiểm tra với len zero
Tôi kiểm tra với kiểm tra chuỗi trống và không trống. Bạn có thể thấy rằng kiểm tra với một chuỗi trống nhanh hơn.
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
Mã
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 != "" { }
là cách tốt nhất, ưa thích và thành ngữ HÔM NAY. Lý do thư viện chuẩn chứa khác là vì nó được viết trước năm 2010 khilen(mystring) == 0
tối ưu hóa có ý nghĩa.