[string] Go에서 빈 문자열을 테스트하는 가장 좋은 방법은 무엇입니까?
비어 있지 않은 문자열을 테스트하는 데 가장 적합한 방법은 무엇입니까?
if len(mystring) > 0 { }
또는:
if mystring != "" { }
또는 다른 것?
답변
두 가지 스타일 모두 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는 golang-nuts 스레드에 씁니다 .
코드를 명확하게하는 것
요소 x를 보려고하면 일반적으로
x == 0 인 경우에도 len (s)> x를 쓰지만
“이 특정 문자열”에 관심 이 있다면 s == “”를 쓰는 경향이 있습니다.성숙한 컴파일러가
len (s) == 0 및 s == “”를 동일한 효율적인 코드로 컴파일한다고 가정하는 것이 합리적 입니다.
…코드를 명확하게하십시오.
Timmmm의 답변 에서 지적했듯이 Go 컴파일러는 두 경우 모두 동일한 코드를 생성합니다.
답변
이것은 조기 미세 최적화로 보입니다. 컴파일러는 두 경우 모두 또는 적어도 두 가지 모두에 대해 동일한 코드를 자유롭게 생성 할 수 있습니다
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!")
}
}
답변
빈 공간과 모든 선행 및 후행 공백을 제거해야한다고 가정합니다.
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
때문에 :
len("") // is 0
len(" ") // one empty space is 1
len(" ") // two empty spaces is 2
답변
현재 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.01ns / op) 만 있습니다.
그리고 전체 로그를 보면 시도 간의 차이가 벤치 마크 함수의 차이보다 큽니다.
또한 후자의 변형이 2 번이 아닌 6 번 증가해야하더라도 BenchmarkStringCheckEq와 BenchmarkStringCheckNe 또는 BenchmarkStringCheckLen 및 BenchmarkStringCheckLenGt간에 측정 가능한 차이가없는 것으로 보입니다.
수정 된 테스트 또는 내부 루프로 테스트를 추가하여 동일한 성능에 대한 확신을 얻을 수 있습니다. 이것은 더 빠릅니다.
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 개의 문자열 (접두사 변형)을 테스트하는 것보다 빈 문자열을 테스트하는 것이 더 빠릅니다.