入力ストリームからバイト配列を作成するために推奨される方法は何ですか?
これが.NET 3.5での現在のソリューションです。
Stream s;
byte[] b;
using (BinaryReader br = new BinaryReader(s))
{
b = br.ReadBytes((int)s.Length);
}
ストリームのチャンクを読み書きする方が良いでしょうか?
入力ストリームからバイト配列を作成するために推奨される方法は何ですか?
これが.NET 3.5での現在のソリューションです。
Stream s;
byte[] b;
using (BinaryReader br = new BinaryReader(s))
{
b = br.ReadBytes((int)s.Length);
}
ストリームのチャンクを読み書きする方が良いでしょうか?
回答:
それは本当にあなたが信頼できるかどうかにかかっていますs.Length
。多くのストリームでは、データの量がわからないだけです。そのような場合-.NET 4以前は-次のようなコードを使用します。
public static byte[] ReadFully(Stream input)
{
byte[] buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
.NET 4 Stream.CopyTo
以降では、を使用します。これは、コード内のループと基本的に同等です。を作成しMemoryStream
、呼び出しstream.CopyTo(ms)
てから戻りms.ToArray()
ます。仕事が終わりました。
なぜ私の回答が他の回答よりも長いのかを説明する必要があります。Stream.Read
要求されたすべてを読み取ることを保証するものではありません。たとえば、ネットワークストリームから読み取る場合、すぐにデータが増える場合でも、1つのパケットの値を読み取ってから戻る可能性があります。BinaryReader.Read
ストリームの最後または指定したサイズまで続行しますが、開始するサイズを知っている必要があります。
上記のメソッドはMemoryStream
、データがなくなるまで読み取り(およびへのコピー)を続けます。次にMemoryStream
、配列のデータのコピーを返すように要求します。はじめのサイズがわかっている場合、または確信が持てないがサイズがわかっていると思われる場合は、MemoryStream
を最初のサイズに構築できます。同様に、最後にチェックを置くことができ、ストリームの長さがバッファーと同じサイズ(によって返されるMemoryStream.GetBuffer
)であれば、バッファーを返すだけです。したがって、上記のコードは完全に最適化されていませんが、少なくとも正しいでしょう。ストリームを閉じる責任はありません-呼び出し側はそれを行う必要があります。
詳細(および代替の実装)については、この記事を参照してください。
16*1024
具体的に理由を尋ねてもらえますか?
ジョンの答えは正しいですが、ジョンはすでにに存在するコードを書き換えていますCopyTo
。したがって、.Net 4ではSandipのソリューションを使用しますが、.Netの以前のバージョンではJonの回答を使用します。Sandipのコードは、 "using"を使用することで改善されます。例外CopyTo
は、多くの場合、非常に可能性が高く、MemoryStream
処理されないままになるためです。
public static byte[] ReadFully(Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
input
がすでにMemorySteam
短絡しているかどうかを確認する価値があるかもしれません。私はそれを渡すことは発信者の愚かであることを知ってMemoryStream
いますが...
MemoryStream
、その後の最適化は、あなたのコンテキストで理にかなっているかどうかだ1コピーするのにかかる時間に対して型変換の何百万人を行うために要する時間を比較したものであるMemoryStream
にし別MemoryStream
。
あなたがすでにそれを持っているMemoryStreamを持っている場合に備えてそれを指摘したいだけですmemorystream.ToArray()
。
また、不明なサブタイプまたは異なるサブタイプのストリームを処理していてMemoryStream
、を受信できる場合は、それらのケースについて上記のメソッドを中継し、次のように他の受け入れられた回答を使用できます。
public static byte[] StreamToByteArray(Stream stream)
{
if (stream is MemoryStream)
{
return ((MemoryStream)stream).ToArray();
}
else
{
// Jon Skeet's accepted answer
return ReadFully(stream);
}
}
MemoryStream
s であるストリームに対してのみ機能します。もちろん、この例は、初期化されていない変数をどのように使用しているかについても、明らかに不完全です。
stream.Seek(1L, SeekOrigin.Begin)
、後で呼び出す前に、ストリームがメモリストリームの場合、他のストリームの場合よりも1バイト多くなります。呼び出し元が現在の位置からストリームの終わりまで読み取ることを期待している場合は、CopyTo
orを使用しないでくださいToArray()
。ほとんどの場合、これは問題にはなりませんが、発信者がこの風変わりな動作を知らない場合、混乱します。
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();
私のカップルセントだけ...私が頻繁に使用する習慣は、カスタムヘルパーとしてこのようなメソッドを整理することです
public static class StreamHelpers
{
public static byte[] ReadFully(this Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
}
名前空間を設定ファイルに追加して、どこでも使用できます
CopyTo
は利用できなかったため、.NET 3.5以下では機能しませんStream
。
拡張機能を使ってさらに洗練させることもできます。
namespace Foo
{
public static class Extensions
{
public static byte[] ToByteArray(this Stream stream)
{
using (stream)
{
using (MemoryStream memStream = new MemoryStream())
{
stream.CopyTo(memStream);
return memStream.ToArray();
}
}
}
}
}
そして、それを通常のメソッドとして呼び出します:
byte[] arr = someStream.ToByteArray()
ボブ(つまり、質問者)のコードでコンパイル時エラーが発生します。Stream.Lengthはlongですが、BinaryReader.ReadBytesは整数パラメーターを取ります。私の場合、長い精度を必要とするのに十分な大きさのストリームを処理することを期待していないため、以下を使用します。
Stream s;
byte[] b;
if (s.Length > int.MaxValue) {
throw new Exception("This stream is larger than the conversion algorithm can currently handle.");
}
using (var br = new BinaryReader(s)) {
b = br.ReadBytes((int)s.Length);
}
誰かがそれを気に入った場合のために、ここでは、MemoryStreamに対する不要なDispose呼び出しのない拡張メソッドとして形成された.NET 4+のみのソリューションを示します。これは絶望的なほど簡単な最適化ですが、MemoryStreamの破棄に失敗しても実際の失敗ではないことに注意してください。
public static class StreamHelpers
{
public static byte[] ReadFully(this Stream input)
{
var ms = new MemoryStream();
input.CopyTo(ms);
return ms.ToArray();
}
}
上記のものは問題ありませんが、SMTP経由でデータを送信すると(必要な場合)、データが破損します。私はバイトごとに正しく送信するのに役立つ別のものに変更しました: '
using System;
using System.IO;
private static byte[] ReadFully(string input)
{
FileStream sourceFile = new FileStream(input, FileMode.Open); //Open streamer
BinaryReader binReader = new BinaryReader(sourceFile);
byte[] output = new byte[sourceFile.Length]; //create byte array of size file
for (long i = 0; i < sourceFile.Length; i++)
output[i] = binReader.ReadByte(); //read until done
sourceFile.Close(); //dispose streamer
binReader.Close(); //dispose reader
return output;
}'
名前空間RestSharp.Extensionsには、メソッドReadAsBytesがあります。このメソッドの内部ではMemoryStreamが使用され、このページのいくつかの例のように同じコードがありますが、RestSharpを使用している場合はこれが最も簡単な方法です。
using RestSharp.Extensions;
var byteArray = inputStream.ReadAsBytes();
これは私が使用し、テストし、うまく機能した機能です。'input'はnullであってはならず、 'input.position'は読み取る前に '0'にリセットする必要があります。そうしないと、読み取りループが中断され、配列に変換するために何も読み取られません。
public static byte[] StreamToByteArray(Stream input)
{
if (input == null)
return null;
byte[] buffer = new byte[16 * 1024];
input.Position = 0;
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
byte[] temp = ms.ToArray();
return temp;
}
}
public static byte[] ToByteArray(Stream stream)
{
if (stream is MemoryStream)
{
return ((MemoryStream)stream).ToArray();
}
else
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
}
私はそれを単一の行で機能させることができました:
byte [] byteArr= ((MemoryStream)localStream).ToArray();
johnnyRoseで明記されているように、上記のコードはMemoryStreamでのみ機能します
localStream
はない場合はどうなりMemoryStream
ますか?このコードは失敗します。
localStream
にはMemoryStream
、しかし、localStream
あるではないMemoryStream
、それはなり失敗します。このコードは正常にコンパイルされますが、の実際のタイプによっては、実行時に失敗する可能性がありlocalStream
ます。基本型を子型に任意にキャストできるとは限りません。詳細はこちら。これは、常にこれを行うことができない理由を説明するもう1つの良い例です。