2015-07-17 8 views
6

Jaki jest najlepszy sposób sprawdzenia, czy ciąg znaków Go i bajt zawierają te same bajty? Najprostszy model str == string(byteSlice) jest niewydajny, ponieważ najpierw kopiuje go.Porównaj kawałek ciągu i bajtu w polu Przejdź bez kopii

Szukałem wersji Equal(a, b []byte), która pobiera ciąg jako argument, ale nie może znaleźć niczego odpowiedniego.

+1

W Go 1.5 nie ma "jest" a [strings.Compare] (https://github.com/golang/go/blob/master/src/strings/compare.go), jednak z bardzo nieefektywnego projektu . – OneOfOne

Odpowiedz

4

Począwszy od idź 1,5 kompilator optymalizuje łańcuch (bajtów) przy porównywaniu do łańcucha przy użyciu stack-allocated temporary. Tak więc od Go 1.5

str == string(byteSlice) 

stał się kanonicznym i skutecznym sposobem porównywania ciągów do bajtów.

+0

Nawet w 1.7, uważam, że porównanie długości pierwszych jest szybsze w przypadku, gdy są różne: 'len (str) == len (byteSlice) && str == string (byteSlice)' - wygląda na to, ponieważ Go tworzy kreację tymczasowego plasterka (bez przydziału, ale wciąż zajmuje trochę czasu), zanim porównuje długość. – thomasrutter

4

Jeśli jesteś na tyle komfortowo z tym, że można to rozbić na nowszej wersji (choć wątpliwe), można użyć unsafe:

func unsafeCompare(a string, b []byte) int { 
    abp := *(*[]byte)(unsafe.Pointer(&a)) 
    return bytes.Compare(abp, b) 
} 

func unsafeEqual(a string, b []byte) bool { 
    bbp := *(*string)(unsafe.Pointer(&b)) 
    return a == bbp 
} 

playground

Benchmarks:

// using: 
// aaa = strings.Repeat("a", 100) 
// bbb = []byte(strings.Repeat("a", 99) + "b") 

// go 1.5 
BenchmarkCopy-8   20000000    75.4 ns/op 
BenchmarkPetersEqual-8 20000000    83.1 ns/op 
BenchmarkUnsafe-8  100000000    12.2 ns/op 
BenchmarkUnsafeEqual-8 200000000    8.94 ns/op 
// go 1.4 
BenchmarkCopy   10000000    233 ns/op 
BenchmarkPetersEqual 20000000    72.3 ns/op 
BenchmarkUnsafe   100000000    15.5 ns/op 
BenchmarkUnsafeEqual 100000000    10.7 ns/op 
+3

Nie sądzę, że nie należy proponować używania pakietu niebezpiecznego dla przedwczesnych optymalizacji. Gdyby OP zademonstrował, że konwersja jest głównym wąskim gardłem jego kodu, być może. – Volker

+0

@Volker wyjaśnił, że dla jego obciążenia 'str == string (byteSlice)' jest nieefektywne, ponieważ kopiuje go, co jest zrozumiałe. – OneOfOne

+2

Jeśli nie jest na swojej gorącej ścieżce, to nie ma znaczenia, czy jest nieefektywna czy bardzo nieefektywna. – Volker

4

The Go Programming Language Specification

String types

Typ ciągu reprezentuje zestaw wartości ciągu. Wartość ciągu to (prawdopodobnie pusta) sekwencja bajtów. Wstępnie zadeklarowany ciąg znaków to ciąg znaków .

Długość napisu s (jego rozmiar w bajtach) można wykryć za pomocą wbudowanej funkcji len w postaci . Dostęp do bajtów ciągu można uzyskać za pomocą liczb całkowitych indeksy od 0 do len (s) -1.

Na przykład

package main 

import "fmt" 

func equal(s string, b []byte) bool { 
    if len(s) != len(b) { 
     return false 
    } 
    for i, x := range b { 
     if x != s[i] { 
      return false 
     } 
    } 
    return true 
} 

func main() { 
    s := "equal" 
    b := []byte(s) 
    fmt.Println(equal(s, b)) 
    s = "not" + s 
    fmt.Println(equal(s, b)) 
} 

wyjściowa:

true 
false 
+2

W przypadku braku natywnej implementacji jest to wystarczająco uniwersalne zamiennik, szczególnie gdy nie jest oczekiwany średni mecz, a pętla kończy się po pierwszym lub drugim znaku –