2012-03-10 4 views
89

私はio.ReadCloserオブジェクト(http.Responseオブジェクト)を持っています。io.ReaderからGoの文字列へ

ストリーム全体をstringオブジェクトに変換する最も効率的な方法は何ですか?

答えて

123

短い答えは、文字列に変換するにはバイト配列の完全なコピーを行う必要があるため、効率的ではないということです。

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

このコピーは保護メカニズムとして実行されます。このコピーは保護メカニズムとして実行されます。文字列は不変です。 []バイトを文字列に変換できる場合は、文字列の内容を変更することができます。ただし、安全でないパッケージを使用してタイプセーフティメカニズムを無効にすることができます。危険なパッケージは、自己責任で使用してください。うまくいけば、名前だけでも十分な警告です。ここで私は安全ではない使用してそれを行うだろうかです:

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

私達は行く、あなたは今、効率的に文字列に自分のバイト配列に変換しています。本当に、これは文字列と呼ぶ型システムを欺くことです。この方法にはいくつかの注意点があります。

  1. これはすべてのgoコンパイラで動作する保証はありません。これはplan-9 gcコンパイラで動作しますが、公式仕様には記載されていない「実装の詳細」に依存しています。これがすべてのアーキテクチャで動作するか、gcで変更されないことを保証することさえできません。言い換えれば、これは悪い考えです。
  2. その文字列は可変です!そのバッファに何か呼び出しを行うと、になります。十分気をつける。

私のアドバイスは、公式の方法に固執することです。コピーを行うのはで、それはで高価ではありません。文字列が大きすぎてコピーを作成できない場合は、文字列にしないでください。

+0

ありがとう、それは本当に詳細な答えです。 "良い"方法は、@ソニアの答えとほぼ同じように見えます(buf.Stringはキャストを内部的に行うだけなので)。 – djd

+1

そして私のバージョンではうまく動作しません。&But.Bytes()からPointerを得ることができないようです。 Go1を使用する。 – sinni800

+0

@ sinni800チップをありがとう。私は、関数の戻り値は扱うことができないことを忘れていたこれは修正されました。 –

0

私はbytes.Buffer構造が好きです。私はそれがReadFromStringの方法を持って参照してください。私は[]バイトで使用しましたが、io.Readerでは使用しませんでした。

4

最も効率的な方法は、stringの代わりに常に[]byteを使用することです。

あなたはio.ReadCloserから受信したデータを印刷する必要がある場合には、fmtパッケージは[]byteを扱うことができますが、fmt実装は内部string[]byteを変換しますので、それは効率的ではありません。この変換を避けるには、type ByteSlice []byteのようなタイプのインタフェースをfmt.Formatterに実装することができます。

+0

は、文字列高価に[]バイトからの変換ですか?私は文字列([]バイト)が[]バイトを実際にコピーしなかったと仮定しましたが、スライス要素を一連のルーンとして解釈しました。それで私はBuffer.String()http://weekly.golang.org/src/pkg/bytes/buffer.go?s=1787:1819#L37を提案したのです。文字列([]バイト)が呼び出されたときに何が起きているのかを知ることは良いことだと思います。 – Nate

+4

'[] byte'から' string'への変換はかなり高速ですが、質問は「最も効率的な方法」について質問していました。現在のところ、Go実行時は '[] byte'を' string'に変換するとき常に新しい 'string'を割り当てます。この理由は、コンパイラは変換後に '[] byte 'が変更されるかどうかを判断する方法を知らないからです。コンパイラの最適化のための余裕があります。 –

63

これまでの回答では、質問全体の「ストリーム全体」には触れていません。私はこれを行う良い方法はioutil.ReadAllだと思います。 rcという名前io.ReaderCloserでは、私が書くでしょう、

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

ありがとう、良い答え。 'buf.ReadFrom()'はストリーム全体をEOFまで読み込むようです。 – djd

+6

面白い: 'ioutil.ReadAll()'の実装を読んだだけで、単に 'bytes.Buffer'の' ReadFrom'をラップします。また、バッファの 'String()'メソッドは 'string'へのキャストを単純なラップで処理するので、2つのアプローチは実質的に同じです! – djd

+1

これは最も優れた、最も簡潔なソリューションです。 – mk12