web-dev-qa-db-fra.com

De io.Reader à chaîne dans Go

J'ai un io.ReadCloser objet (à partir d'un http.Response objet).

Quel est le moyen le plus efficace de convertir le flux entier en un objet string?

116
djd

La réponse courte est que cela ne sera pas efficace car la conversion en chaîne nécessite de faire une copie complète du tableau d'octets. Voici la manière appropriée (non efficace) de faire ce que vous voulez:

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

Cette copie est faite en tant que mécanisme de protection. Les cordes sont immuables. Si vous pouviez convertir un octet [] en chaîne, vous pourriez en modifier le contenu. Cependant, go vous permet de désactiver les mécanismes de sécurité de type à l'aide du package non sécurisé. Utilisez le paquet dangereux à vos risques et périls. Espérons que le nom seul est un avertissement suffisant. Voici comment je le ferais en utilisant unsafe:

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

Voilà, vous avez maintenant converti efficacement votre tableau d'octets en chaîne. En réalité, tout ce que cela fait, c'est de tromper le système de typage en l'appelant une chaîne. Il y a quelques mises en garde à cette méthode:

  1. Rien ne garantit que cela fonctionnera dans tous les compilateurs. Bien que cela fonctionne avec le compilateur plan-9 gc, il repose sur des "détails d'implémentation" non mentionnés dans les spécifications officielles. Vous ne pouvez même pas garantir que cela fonctionnera sur toutes les architectures ou ne sera pas modifié dans gc. En d'autres termes, c'est une mauvaise idée.
  2. Cette chaîne est mutable! Si vous passez des appels sur cette mémoire tampon, will changera la chaîne. Soyez très prudent.

Mon conseil est de s'en tenir à la méthode officielle. Faire une copie n'est pas ça cher et cela ne vaut pas le mal d'être dangereux. Si la chaîne est trop grande pour faire une copie, vous ne devriez pas en faire une chaîne.

158
Stephen Weinberg

Les réponses à ce jour n'ont pas abordé la partie "flux complet" de la question. Je pense que la bonne façon de faire ceci est ioutil.ReadAll. Avec votre io.ReaderCloser Nommé rc, j’écrirais,

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

Le moyen le plus efficace serait de toujours utiliser []byte au lieu de string.

Au cas où vous auriez besoin d’imprimer les données reçues du io.ReadCloser, le package fmt peut gérer []byte, mais cela n’est pas efficace car l’implémentation de fmt convertira en interne []byte à string. Pour éviter cette conversion, vous pouvez implémenter le fmt.Formatter interface pour un type comme type ByteSlice []byte.

5
user811773
data, _ := ioutil.ReadAll(response.Body)
fmt.Println(string(data))
4
yakob abada
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
}
2
Dimchansky

J'aime le bytes.Buffer struct. Je vois qu'il a ReadFrom et String méthodes. Je l'ai utilisé avec un octet [] mais pas un io.Reader.

0
Nate