Mam io.ReadCloserobiekt (z http.Responseobiektu).
Jaki jest najbardziej efektywny sposób przekształcenia całego strumienia w stringobiekt?
Odpowiedzi:
EDYTOWAĆ:
Od 1.10 istnieje strings.Builder. Przykład:
buf := new(strings.Builder)
n, err := io.Copy(buf, r)
// check errors
fmt.Println(buf.String())
NIEAKTUALNE INFORMACJE PONIŻEJ
Krótka odpowiedź jest taka, że nie będzie to efektywne, ponieważ konwersja na łańcuch wymaga wykonania pełnej kopii tablicy bajtów. Oto właściwy (nieefektywny) sposób robienia tego, co chcesz:
buf := new(bytes.Buffer)
buf.ReadFrom(yourReader)
s := buf.String() // Does a complete copy of the bytes in the buffer.
Ta kopia jest wykonywana jako mechanizm ochronny. Ciągi znaków są niezmienne. Gdybyś mógł przekonwertować [] bajt na łańcuch, mógłbyś zmienić zawartość łańcucha. Jednak go pozwala wyłączyć mechanizmy bezpieczeństwa typu przy użyciu niebezpiecznego pakietu. Używaj niebezpiecznego pakietu na własne ryzyko. Miejmy nadzieję, że sama nazwa jest wystarczająco dobrym ostrzeżeniem. Oto, jak zrobiłbym to, używając niebezpiecznego:
buf := new(bytes.Buffer)
buf.ReadFrom(yourReader)
b := buf.Bytes()
s := *(*string)(unsafe.Pointer(&b))
Proszę bardzo, teraz skutecznie przekonwertowałeś swoją tablicę bajtów na łańcuch. Naprawdę wszystko to oszukuje system typów, aby nazwał go łańcuchem. Istnieje kilka zastrzeżeń dotyczących tej metody:
Radzę trzymać się oficjalnej metody. Robi kopię nie jest to drogie i nie warto zło niebezpieczne. Jeśli ciąg jest zbyt duży, aby wykonać kopię, nie powinieneś tworzyć z niego łańcucha.
strings.Builderrobi to skutecznie, upewniając się, że baza []bytenigdy nie przecieka i konwertuje do wersji stringbez kopii w sposób, który będzie obsługiwany w przyszłości. To nie istniało w 2012 roku. Poniższe rozwiązanie @ dimchansky jest poprawne od wersji Go 1.10. Rozważ zmianę!
Dotychczasowe odpowiedzi nie dotyczyły części pytania dotyczącej „całego strumienia”. Myślę, że dobrym sposobem na to jest ioutil.ReadAll. Z twoim io.ReaderCloserimieniem rcnapisałbym,
if b, err := ioutil.ReadAll(rc); err == nil {
return string(b)
} ...
buf.ReadFrom()odczytuje również cały strumień aż do EOF.
ioutil.ReadAll()i po prostu owija bytes.Buffer„s ReadFrom. A String()metoda bufora polega na prostym rzutowaniu dookoła string- więc te dwa podejścia są praktycznie takie same!
data, _ := ioutil.ReadAll(response.Body)
fmt.Println(string(data))
Najbardziej efektywnym sposobem byłoby zawsze używanie []bytezamiast string.
W przypadku, gdy musisz wydrukować dane otrzymane z io.ReadCloser, fmtpakiet może obsłużyć []byte, ale nie jest to wydajne, ponieważ fmtimplementacja zostanie wewnętrznie przekonwertowana []bytena string. Aby uniknąć tej konwersji, możesz zaimplementować fmt.Formatterinterfejs dla typu type ByteSlice []byte.
[]bytena stringjest dość szybka, ale pytanie dotyczyło „najbardziej wydajnego sposobu”. Obecnie środowisko wykonawcze Go zawsze przydziela nowy stringpodczas konwersji []bytedo string. Powodem tego jest to, że kompilator nie wie, jak określić, czy []byteplik zostanie zmodyfikowany po konwersji. Jest tu miejsce na optymalizacje kompilatora.
func copyToString(r io.Reader) (res string, err error) {
var sb strings.Builder
if _, err = io.Copy(&sb, r); err == nil {
res = sb.String()
}
return
}
var b bytes.Buffer
b.ReadFrom(r)
// b.String()
Podoba mi się struktura bytes.Buffer . Widzę, że ma metody ReadFrom i String . Użyłem go z [] bajtem, ale nie z io.Reader.