Mam io.ReadCloser
obiekt (z http.Response
obiektu).
Jaki jest najbardziej efektywny sposób przekształcenia całego strumienia w string
obiekt?
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.Builder
robi to skutecznie, upewniając się, że baza []byte
nigdy nie przecieka i konwertuje do wersji string
bez 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.ReaderCloser
imieniem rc
napisał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 []byte
zamiast string
.
W przypadku, gdy musisz wydrukować dane otrzymane z io.ReadCloser
, fmt
pakiet może obsłużyć []byte
, ale nie jest to wydajne, ponieważ fmt
implementacja zostanie wewnętrznie przekonwertowana []byte
na string
. Aby uniknąć tej konwersji, możesz zaimplementować fmt.Formatter
interfejs dla typu type ByteSlice []byte
.
[]byte
na string
jest dość szybka, ale pytanie dotyczyło „najbardziej wydajnego sposobu”. Obecnie środowisko wykonawcze Go zawsze przydziela nowy string
podczas konwersji []byte
do string
. Powodem tego jest to, że kompilator nie wie, jak określić, czy []byte
plik 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.