Из io.Reader в строку в Go - PullRequest
       2

Из io.Reader в строку в Go

116 голосов
/ 10 марта 2012

У меня есть объект io.ReadCloser (из объекта http.Response).

Какой самый эффективный способ преобразовать весь поток в объект string?

Ответы [ 6 ]

156 голосов
/ 10 марта 2012

Короткий ответ: он не будет эффективным, потому что преобразование в строку требует создания полной копии байтового массива. Вот правильный (неэффективный) способ сделать то, что вы хотите:

buf := new(bytes.Buffer)
buf.ReadFrom(yourReader)
s := buf.String() // Does a complete copy of the bytes in the buffer.

Эта копия сделана как механизм защиты. Строки неизменны. Если бы вы могли преобразовать байт [] в строку, вы могли бы изменить содержимое строки. Однако go позволяет отключить механизмы безопасности типов с помощью небезопасного пакета. Используйте небезопасный пакет на свой страх и риск. Надеюсь, одно только имя является достаточно хорошим предупреждением. Вот как я бы сделал это, используя unsafe:

buf := new(bytes.Buffer)
buf.ReadFrom(yourReader)
b := buf.Bytes()
s := *(*string)(unsafe.Pointer(&b))

Итак, вы эффективно преобразовали свой байтовый массив в строку. На самом деле все, что это делает - это обманывает систему типов, называя ее строкой. У этого метода есть пара предостережений:

  1. Нет никаких гарантий, что это будет работать во всех компиляторах go. Хотя это работает с компилятором gc plan-9, оно опирается на «детали реализации», не упомянутые в официальной спецификации. Вы даже не можете гарантировать, что это будет работать на всех архитектурах или не будет изменено в gc. Другими словами, это плохая идея.
  2. Эта строка изменчива! Если вы сделаете какие-либо вызовы в этом буфере, изменит строку. Будьте очень осторожны.

Мой совет - придерживаться официального метода. Создание копии не стоит , что дорого и не стоит зла ​​небезопасного. Если строка слишком велика для копирования, вам не следует превращать ее в строку.

90 голосов
/ 11 марта 2012

Ответы до сих пор не затронули часть вопроса «весь поток». Я думаю, что хороший способ сделать это - ioutil.ReadAll. С вашим io.ReaderCloser именем rc я бы написал,

if b, err := ioutil.ReadAll(rc); err == nil {
    return string(b)
} ...
5 голосов
/ 10 марта 2012

Самый эффективный способ - всегда использовать []byte вместо string.

. Если вам нужно распечатать данные, полученные из io.ReadCloser, пакет fmt может обработать []byte, но это неэффективно, потому что реализация fmt внутренне преобразует []byte в string.Чтобы избежать этого преобразования, вы можете реализовать интерфейс fmt.Formatter для типа, подобного type ByteSlice []byte.

4 голосов
/ 01 июля 2018
data, _ := ioutil.ReadAll(response.Body)
fmt.Println(string(data))
1 голос
/ 04 октября 2018
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
}
0 голосов
/ 10 марта 2012

Мне нравится структура bytes.Buffer . Я вижу, у него есть ReadFrom и String методы. Я использовал его с байтом [], но не с io.Reader.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...