很难有效地进行插入。特别是如果您想对同一个流进行多次插入。您可以编写自己的Stream
类来跟踪您所做的任何插入,并在实际上没有插入更改时假装已插入更改。例如,如果您有一个A
at 位置 10 并且您在该位置插入一个X
。您的类可以,而不是将插入写入流并移动所有后续字节,而是X
在您请求位置 10 时返回,并A
在您请求位置 11 时从基础流返回(而在基础流中它仍位于位置 10)。然后当你打电话Flush
或Close
在流上,整个更改的流一次全部刷新到底层流。这需要一些非常仔细的工程才能做到正确,因为您可能在插入中插入,以任何顺序插入,等等......
这是我为您编写的一些代码,它将任意字节插入流中并移动所有后续字节,而无需任何巧妙的缓存。不幸的是,它不使用 aBinaryWriter
而是直接在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
对象。如果您以后决定仍然需要 a BinaryWriter
,则可以使用将流提供给BinaryWriter
构造函数。要从 writer 获取流,请使用BinaryWriter.BaseStream。请注意,关闭 aBinaryWriter
也会关闭底层流,除非您使用NonClosingStreamWrapper
MiscUtil库中的