2012-03-10 7 views
89

Ho un io.ReadCloser oggetto (da un oggetto http.Response).Da io.Reader a stringa nel Go

Qual è il modo più efficace per convertire l'intero flusso di un oggetto string?

risposta

123

La risposta breve è che non sarà efficace, perché la conversione in una stringa richiede di fare una copia completa della matrice di byte. Ecco il modo corretto (non efficiente) di fare quello che vuoi:

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

Questa copia viene eseguita come meccanismo di protezione. Le stringhe sono immutabili. Se potessi convertire un [] byte in una stringa, potresti cambiare il contenuto della stringa. Tuttavia, go consente di disabilitare i meccanismi di sicurezza del tipo utilizzando il pacchetto non sicuro. Utilizzare il pacchetto pericoloso a proprio rischio. Spero che il nome sia un avvertimento abbastanza buono. Ecco come lo farei con pericoloso:

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

Ci andiamo, avete ora convertita efficacemente l'array di byte in una stringa. In realtà, tutto ciò che fa è ingannare il sistema di tipi chiamandolo stringa. Ci sono un paio di avvertimenti a questo metodo:

  1. Non ci sono garanzie che questo funzionerà in tutti i compilatori. Sebbene funzioni con il compilatore plan-9 gc, si basa su "dettagli di implementazione" non menzionati nelle specifiche ufficiali. Non puoi nemmeno garantire che funzioni su tutte le architetture o non sia modificato in gc. In altre parole, questa è una cattiva idea.
  2. Quella stringa è mutabile! Se si effettuano chiamate su tale buffer, lo sostituirà la stringa. Essere molto attenti.

mio consiglio è di attenersi al metodo ufficiale. Fare una copia non è che costoso e non vale i mali di non sicuro. Se la stringa è troppo grande per eseguire una copia, non dovresti trasformarla in una stringa.

+0

Grazie, questa è una risposta molto dettagliata. Il modo "buono" sembra approssimativamente equivalente alla risposta di @ Sonia (dal momento che buf.String esegue il cast solo internamente). – djd

+1

E non funziona nemmeno con la mia versione, sembra non essere in grado di ottenere un puntatore da & but.Bytes(). Utilizzando Go1. – sinni800

+0

@ sinni800 Grazie per il suggerimento. Ho dimenticato che i ritorni di funzione non erano indirizzabili. Ora è risolto. –

4

Il modo più efficiente sarebbe utilizzare sempre []byte anziché string.

In caso di necessità di stampare i dati ricevuti dal io.ReadCloser, il pacchetto fmt in grado di gestire []byte, ma non è efficace perché l'implementazione fmt sarà internamente convertire []byte-string. Per evitare questa conversione, è possibile implementare l'interfaccia fmt.Formatter per un tipo come type ByteSlice []byte.

+0

è la conversione da [] byte a stringa costoso? Ho assunto che la stringa ([] byte) non copiasse effettivamente il [] byte, ma interpretasse gli elementi della sezione come una serie di rune. Questo è il motivo per cui ho suggerito Buffer.String() http://weekly.golang.org/src/pkg/bytes/buffer.go?s=1787:1819#L37. Immagino sarebbe bello sapere cosa sta succedendo quando viene chiamata string ([] byte). – Nate

+4

La conversione da '[] byte' a' string' è ragionevolmente veloce, ma la domanda stava chiedendo "il modo più efficiente". Attualmente, il run-time Go assegnerà sempre una nuova 'stringa' quando converte' [] byte' in 'stringa'. La ragione di ciò è che il compilatore non sa come determinare se il '[] byte' sarà modificato dopo la conversione. C'è spazio per le ottimizzazioni del compilatore qui. –

63

risposte finora non hanno concesso la parte "intero flusso" della questione. Penso che il modo migliore per farlo sia ioutil.ReadAll. Con il vostro io.ReaderCloser chiamato rc, vorrei scrivere,

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

Grazie, buona risposta. Sembra che "buf.ReadFrom()" legga anche l'intero stream fino a EOF. – djd

+6

Che buffo: ho appena letto l'implementazione di 'ioutil.ReadAll()' e si limita a racchiudere il file 'ReadFrom' di' bytes.Buffer'. E il metodo 'String()' del buffer è un semplice wrap around cast per 'string' - quindi i due approcci sono praticamente gli stessi! – djd

+1

Questa è la soluzione migliore e più concisa. – mk12