From io.Reader to string in Go

huangapple go评论80阅读模式
英文:

From io.Reader to string in Go

问题

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

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

英文:

I have an io.ReadCloser object (from an http.Response object).

What's the most efficient way to convert the entire stream to a string object?

答案1

得分: 239

自1.10版本以来,存在strings.Builder。示例:

buf := new(strings.Builder)
n, err := io.Copy(buf, r)
// 检查错误
fmt.Println(buf.String())

以下信息已过时

简短的答案是,这样做效率不高,因为将其转换为字符串需要对字节数组进行完全复制。以下是实现你想要的(非高效)方法:

buf := new(bytes.Buffer)
buf.ReadFrom(yourReader)
s := buf.String() // 对缓冲区中的字节进行完全复制。

这种复制是作为一种保护机制进行的字符串是不可变的如果你可以将[]byte转换为字符串那么你可以更改字符串的内容然而Go允许你使用unsafe包禁用类型安全机制使用unsafe包要自担风险希望仅凭名称就足以作为一个很好的警告以下是我使用unsafe的方法

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

现在你已经高效地将字节数组转换为字符串实际上所有这个方法做的就是欺骗类型系统将其称为字符串这种方法有一些注意事项

1. 不能保证这在所有Go编译器中都能正常工作虽然这在plan-9 gc编译器中有效但它依赖于官方规范中未提及的实现细节”。你甚至不能保证这在所有体系结构上都能正常工作或者在gc中不会被更改换句话说这是一个坏主意
2. 那个字符串是可变的如果对该缓冲区进行任何调用它将改变字符串要非常小心

我的建议是坚持使用官方方法进行复制并不是非常昂贵而且不值得使用unsafe带来的问题如果字符串太大而无法复制那么你就不应该将其转换为字符串

<details>
<summary>英文:</summary>

**EDIT:**

Since 1.10, strings.Builder exists. Example:

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


---
**OUTDATED INFORMATION BELOW**

The short answer is that it it will not be efficient because converting to a string requires doing a complete copy of the byte array. Here is the proper (non-efficient) way to do what you want:

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

This copy is done as a protection mechanism. Strings are immutable. If you could convert a []byte to a string, you could change the contents of the string. However, go allows you to disable the type safety mechanisms using the unsafe package. Use the unsafe package at your own risk. Hopefully the name alone is a good enough warning. Here is how I would do it using unsafe:

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

There we go, you have now efficiently converted your byte array to a string. Really, all this does is trick the type system into calling it a string. There are a couple caveats to this method:

 1. There are no guarantees this will work in all go compilers. While this works with the plan-9 gc compiler, it relies on &quot;implementation details&quot; not mentioned in the official spec. You can not even guarantee that this will work on all architectures or not be changed in gc. In other words, this is a bad idea.
 2. That string is mutable! If you make any calls on that buffer it _will_ change the string. Be very careful.

My advice is to stick to the official method. Doing a copy is not _that_ expensive and it is not worth the evils of unsafe. If the string is too large to do a copy, you should not be making it into a string.

</details>



# 答案2
**得分**: 159

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

Go >= v1.16
```go
if b, err := io.ReadAll(rc); err == nil {
    return string(b)
} ...

Go <= v1.15

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

Answers so far haven't addressed the "entire stream" part of the question. I think the good way to do this is ioutil.ReadAll. With your io.ReaderCloser named rc, I would write,

Go >= v1.16

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

Go <= v1.15

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

答案3

得分: 15

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

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

答案4

得分: 6

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
}

英文:
func copyToString(r io.Reader) (res string, err error) {
	var sb strings.Builder
	if _, err = io.Copy(&amp;sb, r); err == nil {
		res = sb.String()
	}
	return
}

答案5

得分: 5

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

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

英文:

The most efficient way would be to always use []byte instead of string.

In case you need to print data received from the io.ReadCloser, the fmt package can handle []byte, but it isn't efficient because the fmt implementation will internally convert []byte to string. In order to avoid this conversion, you can implement the fmt.Formatter interface for a type like type ByteSlice []byte.

答案6

得分: 2

var b bytes.Buffer
b.ReadFrom(r)

// b.String()

英文:
var b bytes.Buffer
b.ReadFrom(r)

// b.String()

答案7

得分: 0

我喜欢 bytes.Buffer 结构体。我看到它有 ReadFromString 方法。我已经用过它来处理 []byte,但还没有用过 io.Reader。

英文:

I like the bytes.Buffer struct. I see it has ReadFrom and String methods. I've used it with a []byte but not an io.Reader.

huangapple
  • 本文由 发表于 2012年3月10日 13:19:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/9644139.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定