[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는 다른 코드를 생성하지만 간신히 누군가가 사용하므로 걱정하지 않습니다.

https://godbolt.org/z/fib1x1


답변

아래와 같은 기능을 사용하는 것이 더 깨끗하고 오류가 덜 발생합니다.

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 개의 문자열 (접두사 변형)을 테스트하는 것보다 빈 문자열을 테스트하는 것이 더 빠릅니다.