От io.Reader до строки в Go

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

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

Ответ 1

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

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

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

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

Идем дальше, теперь вы эффективно преобразовали массив байтов в строку. В самом деле, все это делает трюк системы типа, называя его строку. Есть несколько предостережений для этого метода:

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

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

Ответ 2

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

if b, err := ioutil.ReadAll(rc); err == nil {
    return string(b)
} ...

Ответ 3

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

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

Ответ 4

data, _ := ioutil.ReadAll(response.Body)
fmt.Println(string(data))

Ответ 5

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
}

Ответ 6

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

Ответ 7

var b bytes.Buffer
b.ReadFrom(r)

// b.String()