从 io.Reader 到 Go 中的 string

我有一个 io.ReadCloser对象(来自一个 http.Response对象)。

将整个流转换为 string对象的最有效方法是什么?

142427 次浏览

我喜欢 字节缓冲区结构。我看到它有 读取自绳子方法。我用的是字节[]但不是输入输出。读者。

最有效的方法是始终使用 []byte而不是 string

如果您需要打印从 io.ReadCloser接收到的数据,fmt包可以处理 []byte,但是效率不高,因为 fmt实现将在内部将 []byte转换为 string。为了避免这种转换,您可以为类似于 type ByteSlice []byte的类型实现 fmt.Formatter接口。

编辑:

从1.10开始,字符串.Builder 就存在了。例如:

buf := new(strings.Builder)
n, err := io.Copy(buf, r)
// check errors
fmt.Println(buf.String())

过时资料如下

简短的回答是,这样做效率不高,因为转换为字符串需要完成字节数组的完整副本。以下是做你想做的事情的正确(非有效)方法:

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))

现在,您已经有效地将字节数组转换为字符串。实际上,这样做只是欺骗类型系统将其调用为字符串。这种方法有几个注意事项:

  1. 这并不能保证在所有的 go 编译器中都能正常工作。虽然可以使用 plan-9 gc 编译器,但它依赖于官方规范中没有提到的“实现细节”。您甚至不能保证这将适用于所有体系结构,或者不在 gc 中进行更改。换句话说,这是个坏主意。
  2. 这个字符串是可变的! 如果你对这个缓冲区进行任何调用,它就会 威尔改变这个字符串。要非常小心。

我的建议是坚持官方的方法。做一个副本是不是昂贵的 那个和它是不值得的邪恶的不安全。如果字符串太大而无法复制,则不应将其制作成字符串。

到目前为止,答案还没有回答这个问题的“整个流”部分。我认为这样做的好方法是 ioutil.ReadAll。用你的 io.ReaderCloser命名为 rc,我会写,

Go > = v1.16

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

去 < = v1.15

if b, err := ioutil.ReadAll(rc); err == nil {
return string(b)
} ...
data, _ := ioutil.ReadAll(response.Body)
fmt.Println(string(data))
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()