从io.Reader到Go中的字符串


129

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

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

Answers:


175

编辑:

从1.10开始,strings.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.

此副本是作为保护机制完成的。字符串是不可变的。如果可以将[] byte转换为字符串,则可以更改字符串的内容。但是,go允许您使用unsafe软件包禁用类型安全机制。使用不安全的包装,后果自负。希望仅此名称就足够了。这是我使用不安全方法的方法:

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

到此为止,您现在已经有效地将字节数组转换为字符串。确实,所有这些都是在欺骗类型系统将其称为字符串。此方法有两个警告:

  1. 不能保证这将在所有go编译器中都有效。尽管这可以与plan-9 gc编译器一起使用,但它依赖于官方规范中未提及的“实现细节”。您甚至不能保证这将在所有体系结构上都适用或在gc中不会更改。换句话说,这是一个坏主意。
  2. 那串是可变的!如果您对该缓冲区进行任何调用,它将更改字符串。要特别小心。

我的建议是坚持官方方法。进行复制并没有那么昂贵,而且不值得进行不安全的弊端。如果字符串太大而无法进行复制,则不应将其制成字符串。


谢谢,这是一个非常详细的答案。“好的”方式似乎也大致相当于@Sonia的答案(因为buf.String仅在内部进行强制转换)。
2012年

1
而且它甚至不适用于我的版本,似乎无法从&but.Bytes()获取指针。使用Go1。
sinni800 2012年

@ sinni800感谢您的提示。我忘了函数返回无法解决。现在已修复。
史蒂芬·温伯格

3
好的计算机在复制字节块时非常快。鉴于这是一个http请求,我无法想象这样一种情况:传输延迟不会比复制字节数组所需的琐碎时间大几千倍。任何功能语言都可以在各处复制这种不可变的内容,并且运行速度仍然很快。

这个答案是过时的。strings.Builder通过确保底层[]byte永不泄漏并以string支持将来的方式转换为无副本,从而有效地做到了这一点。这在2012年不存在。@dimchansky的以下解决方案自Go 1.10起就是正确的解决方案。请考虑修改!
Nuno Cruces

102

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

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

2
谢谢,好的答案。似乎buf.ReadFrom()还读取了整个流,直至达到EOF。
2012年

8
多么有趣:我刚刚阅读的实现,ioutil.ReadAll()它只是包装bytes.BufferReadFrom。而且缓冲区的String()方法是将对象强制转换为简单包装的方法string-因此这两种方法实际上是相同的!
2012年

1
这是最好,最简洁的解决方案。
mk12 2013年

1
我做到了,并且...第一次。出于某种原因,在读取字符串之后,后续读取将返回一个空字符串。不知道为什么。
Aldo'xoen'Giambelluca'2

1
@ Aldo'xoen'Giambelluca ReadAll占用了阅读器,因此在下一次调用时,没有任何内容可供阅读。
DanneJ


5

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

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


从[] byte到字符串的转换是否昂贵?我以为string([] byte)实际上并没有复制[] byte,而只是将slice元素解释为一系列符文。这就是为什么我建议Buffer.String()weekly.golang.org/src/pkg/bytes/buffer.go?s=1787:1819#L37。我想最好知道调用string([] byte)时发生了什么。
2012年

4
[]byte到的转换string速度相当快,但是问题是关于“最有效的方式”的问题。目前,围棋运行时总是会分配一个新的string转换时[]bytestring。这样做的原因是,编译器不知道如何确定[]byte转换后是否会修改。这里有一些编译器优化的空间。

3
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
}


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.