IT story

Go에서 빈 문자열을 테스트하는 가장 좋은 방법은 무엇입니까?

hot-time 2020. 5. 1. 08:10
반응형

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


의견을 더 추가하려면

주로 성능 테스트 방법에 대해 설명합니다.

다음 코드로 테스트했습니다.

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간에 측정 가능한 차이가없는 것으로 보입니다.

You can try to get some confidence about equal performance by adding tests with modified test or inner loop. This is faster:

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)
    }
}

This is not faster:

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)
    }
}

Both variants are usually faster or slower than difference between main tests.

It would also good to generate test strings (ss) using string generator with relevant distribution. And have variable lengths too.

So I don't have any confidence of performance difference between main methods to test empty string in go.

And I can state with some confidence, it is faster not to test empty string at all than test empty string. And also it is faster to test empty string than to test 1 char string (prefix variant).


It would be cleaner and less error-prone to use a function like the one below:

func empty(s string) bool {
    return len(strings.TrimSpace(s)) == 0
}

This would be more performant than trimming the whole string, since you only need to check for at least a single non-space character existing

// 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
}

I think the best way is to compare with blank string

BenchmarkStringCheck1 is checking with blank string

BenchmarkStringCheck2 is checking with len zero

I check with the empty and non-empty string checking. You can see that checking with a blank string is faster.

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

Code

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 {

        }
    }
}

참고URL : https://stackoverflow.com/questions/18594330/what-is-the-best-way-to-test-for-an-empty-string-in-go

반응형