programing

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

i4 2023. 4. 21. 20:14
반응형

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

빈 문자열이 아닌 문자열(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

둘 다 관용적이고 충분히 명확하다.그것은 개인적인 취향과 명확성의 문제이다.

러스 콕스는 골랑넛 실로 다음과 같이 쓰고 있다.

암호를 명확하게 하는 사람.
x 요소를 살펴보려고 하면 보통 다음과 같이 씁니다.
len(s) > x(x ==0인 경우에도 가능하지만,
"이 특정 문자열입니까?" 라고 쓰는 경향이 있습니다. s == " " " " "

성숙한 컴파일러가 컴파일러를 할 것으로 가정하는 것이 타당합니다.
len(s) == 0 및 s == "을(를) 동일한 효율적인 코드로 변환합니다.
...

코드를 명확히 합니다.

Timmmm의 답변에서 지적된 바와 같이 Go 컴파일러는 두 경우 모두 동일한 코드를 생성합니다.

이것은 시기상조인 미세 최적화인 것 같습니다.컴파일러는 양쪽 케이스 또는 적어도 이 두 가지 케이스에 대해 동일한 코드를 자유롭게 생성할 수 있습니다.

if len(s) != 0 { ... }

그리고.

if s != "" { ... }

의미가 확실히 같기 때문입니다.

빈 공간과 선행 및 후행 공백이 모두 삭제되어야 한다고 가정합니다.

import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }

이유:
len("") // is 0
len(" ") // one empty space is 1
len(" ") // two empty spaces is 2

길이를 확인하는 것이 좋은 답변이지만 공백만 있는 "빈" 문자열도 고려할 수 있습니다."기술적으로" 비어 있는 것은 아니지만 다음 사항을 확인해야 합니다.

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

현재 Go 컴파일러는 두 경우 모두 동일한 코드를 생성하므로 취향에 따라 다릅니다.GCCGo는 다른 코드를 생성하지만, 거의 아무도 사용하지 않기 때문에 나는 그것에 대해 걱정하지 않는다.

https://godbolt.org/z/fib1x1

공식 지침 및 성능 관점에서 볼 때 동일한 것으로 보입니다(ANISUS 응답). 구문론적 이점 때문에 s != "가 더 좋습니다. s != "는 변수가 문자열이 아닌 경우 컴파일 시 실패하며, len(s) == 0은 다른 여러 데이터 유형으로 전달됩니다.

생각합니다== ""보다 빠르고 읽기 쉽게 할 수 있습니다.

package main 

import(
    "fmt"
)
func main() {
    n := 1
    s:=""
    if len(s)==0{
        n=2
    }
    fmt.Println("%d", n)
}

언제dlv debug playground.gocmp with len(s) 및 ==" " " " " 이 s == " " " 상황이 발생하였습니다.

    playground.go:6         0x1008d9d20     810b40f9        MOVD 16(R28), R1       
    playground.go:6         0x1008d9d24     e28300d1        SUB $32, RSP, R2       
    playground.go:6         0x1008d9d28     5f0001eb        CMP R1, R2             
    playground.go:6         0x1008d9d2c     09070054        BLS 56(PC)             
    playground.go:6         0x1008d9d30*    fe0f16f8        MOVD.W R30, -160(RSP)  

    playground.go:6         0x1008d9d34     fd831ff8        MOVD R29, -8(RSP)      
    playground.go:6         0x1008d9d38     fd2300d1        SUB $8, RSP, R29       
    playground.go:7         0x1008d9d3c     e00340b2        ORR $1, ZR, R0         

    playground.go:7         0x1008d9d40     e01f00f9        MOVD R0, 56(RSP)       
    playground.go:8         0x1008d9d44     ff7f05a9        STP (ZR, ZR), 80(RSP)  

    playground.go:9         0x1008d9d48     01000014        JMP 1(PC)                        
    playground.go:10        0x1008d9d4c     e0037fb2        ORR $2, ZR, R0         

len(s)==0 상황

    playground.go:6         0x100761d20     810b40f9        MOVD 16(R28), R1       
    playground.go:6         0x100761d24     e2c300d1        SUB $48, RSP, R2       
    playground.go:6         0x100761d28     5f0001eb        CMP R1, R2             
    playground.go:6         0x100761d2c     29070054        BLS 57(PC)             
    playground.go:6         0x100761d30*    fe0f15f8        MOVD.W R30, -176(RSP)  

    playground.go:6         0x100761d34     fd831ff8        MOVD R29, -8(RSP)      
    playground.go:6         0x100761d38     fd2300d1        SUB $8, RSP, R29       
    playground.go:7         0x100761d3c     e00340b2        ORR $1, ZR, R0         

    playground.go:7         0x100761d40     e02300f9        MOVD R0, 64(RSP)       
    playground.go:8         0x100761d44     ff7f06a9        STP (ZR, ZR), 96(RSP)  
    playground.go:9         0x100761d48     ff2700f9        MOVD ZR, 72(RSP)       

    playground.go:9         0x100761d4c     01000014        JMP 1(PC)              
    playground.go:10        0x100761d50     e0037fb2        ORR $2, ZR, R0         
    playground.go:10        0x100761d54     e02300f9        MOVD R0, 64(RSP)       
    playground.go:10        0x100761d58     01000014        JMP 1(PC)      
    playground.go:6         0x104855d2c     09070054        BLS 56(PC)        

블록 따옴표

다음과 같은 기능을 사용하는 것이 보다 깨끗하고 오류 발생률이 낮습니다.

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)에 불과합니다.

전체 로그를 보면 벤치마크 함수 간의 차이보다 시도 간의 차이가 더 큽니다.

또한 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글자 문자열(프리픽스 배리언트)을 테스트하는 것보다 빠릅니다.

공백이 없는 문자가 하나 이상 존재하는지 확인만 하면 되므로 전체 문자열을 잘라내는 것보다 성능이 향상됩니다.

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

빈 줄과 비교하는 것이 가장 좋은 방법이라고 생각합니다.

BenchmarkStringCheck1이 빈 문자열로 확인 중입니다.

BenchmarkStringCheck2는 len 0을 체크하고 있습니다.

빈 문자열 체크와 빈 문자열 체크로 확인합니다.빈 문자열로 확인하는 것이 더 빠르다는 것을 알 수 있습니다.

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

코드

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

반응형