効率的に挿入するのは難しいです。特に、同じストリームに複数の挿入を行いたい場合。Stream
自分が行った挿入を追跡し、実際には挿入されていないときに変更を挿入したふりをする独自のクラスを作成できます。たとえば、A
位置10があり、その位置にを挿入する場合ですX
。クラスは、ストリームに挿入を書き込んで後続のすべてのバイトを移動する代わりに、X
位置10を要求するたびにA
、位置11を要求するときに基になるストリームから戻ることができます(基になるストリームでは、位置10のままです)。それからあなたが電話するときFlush
またはClose
ストリームでは、変更されたストリーム全体が、基になるストリームに一度にフラッシュされます。これを正しく行うには、非常に注意深いエンジニアリングが必要です。挿入に挿入したり、任意の順序で挿入したりする可能性があるためです。
これは、ストリームに任意のバイトを挿入し、巧妙なキャッシュなしで後続のすべてのバイトを移動する、私が作成したコードです。残念ながら、それはを使用しませんが、コーディングが簡単だったため、(シーク可能である必要があります)にBinaryWriter
直接機能します。Stream
ストリームの現在の位置に挿入され、その後、挿入されたバイトの直後の位置になります。Encoding
文字列を挿入するには、クラスを使用して文字列をバイトに変換します。
public static class StreamExtensions
{
/// <summary>
/// Inserts the specified binary data at the current position in the stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="array">The array.</param>
/// <remarks>
/// <para>Note that this is a very expensive operation, as all the data following the insertion point has to
/// be moved.</para>
/// <para>When this method returns, the writer will be positioned at the end of the inserted data.</para>
/// </remarks>
public static void Insert(this Stream stream, byte[] array)
{
#region Contract
if (stream == null)
throw new ArgumentNullException("writer");
if (array == null)
throw new ArgumentNullException("array");
if (!stream.CanRead || !stream.CanWrite || !stream.CanSeek)
throw new ArgumentException("writer");
#endregion
long originalPosition = stream.Position;
long readingPosition = originalPosition;
long writingPosition = originalPosition;
int length = array.Length;
int bufferSize = 4096;
while (bufferSize < length)
bufferSize *= 2;
int currentBuffer = 1;
int[] bufferContents = new int[2];
byte[][] buffers = new [] { new byte[bufferSize], new byte[bufferSize] };
Array.Copy(array, buffers[1 - currentBuffer], array.Length);
bufferContents[1 - currentBuffer] = array.Length;
bool done;
do
{
bufferContents[currentBuffer] = ReadBlock(stream, ref readingPosition, buffers[currentBuffer], out done);
// Switch buffers.
currentBuffer = 1 - currentBuffer;
WriteBlock(stream, ref writingPosition, buffers[currentBuffer], bufferContents[currentBuffer]);
} while (!done);
// Switch buffers.
currentBuffer = 1 - currentBuffer;
// Write the remaining data.
WriteBlock(stream, ref writingPosition, buffers[currentBuffer], bufferContents[currentBuffer]);
stream.Position = originalPosition + length;
}
private static void WriteBlock(Stream stream, ref long writingPosition, byte[] buffer, int length)
{
stream.Position = writingPosition;
stream.Write(buffer, 0, length);
writingPosition += length;
}
private static int ReadBlock(Stream stream, ref long readingPosition, byte[] buffer, out bool done)
{
stream.Position = readingPosition;
int bufferContent = 0;
int read;
do
{
read = stream.Read(buffer, bufferContent, buffer.Length - bufferContent);
bufferContent += read;
readingPosition += read;
} while (read > 0 && read < buffer.Length);
done = read == 0;
return bufferContent;
}
}
このメソッドを使用するには、コードをコードと同じ名前空間に貼り付けてから、次のようにします。
Stream someStream = ...;
byte[] dataToInsert = new byte[]{ 0xDE, 0xAD, 0xBE, 0xEF };
someStream.Position = 5;
someStream.Insert(dataToInsert);
空のファイルを作成するためにそれを使用したため、あなたが持っていると言うのでBinaryWriter
、このクラスで作業できるようにするには、実際のが必要になりますStream
。代わりに静的File.Create ()メソッドとFile.Open()メソッドを使用でき、それらはオブジェクトを返しFileStream
ます。後でまだ必要であると判断した場合は、コンストラクターBinaryWriter
にストリームを提供することを使用できます。BinaryWriter
ライターからストリームを取得するには、BinaryWriter.BaseStreamを使用します。MiscUtilライブラリのを使用しBinaryWriter
てそれを防止しない限り、aを閉じると基になるストリームも閉じることに注意してください。NonClosingStreamWrapper