3

これはC#関連です。最後の 16 バイトを除いて、ソース ストリーム全体を宛先ストリームにコピーする必要がある場合があります。

編集: ストリームは最大 40 GB の範囲になる可能性があるため、静的な byte[] 割り当てを行うことはできません (例: .ToArray())

MSDN のドキュメントを見ると、戻り値が 0 の場合にのみストリームの終わりを確実に判断できるようです。0the requested size

現在、次のようにすべてのバイトをコピーします。inStreamおよびoutStream一般的です-メモリ、ディスク、またはネットワークストリームにすることができます(実際にはそれ以上のものもあります)。

public static void StreamCopy(Stream inStream, Stream outStream)
{
    var buffer = new byte[8*1024];
    var last16Bytes = new byte[16];
    int bytesRead;
    while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0)
    {
        outStream.Write(buffer, 0, bytesRead);
    }
    // Issues:
    // 1. We already wrote the last 16 bytes into 
    //    outStream (possibly over the n/w)
    // 2. last16Bytes = ? (inStream may not necessarily support rewinding)
}

最後の 16 個を除くすべてを確実にコピーするための信頼できる方法は何ですか? inStream でPositionとを使用することは考えられますが、 MSDNには落とし穴があります。Length

Stream から派生したクラスがシークをサポートしていない場合、Length、SetLength、Position、および Seek を呼び出すと、NotSupportedException がスローされます。.

4

3 に答える 3

2

次のソリューションは高速でテスト済みです。それが役に立つことを願っています。すでに念頭に置いていたダブルバッファリングのアイデアを使用します。 EDIT : 最初の反復を残りの反復から分離する条件を削除する単純化されたループ。

public static void StreamCopy(Stream inStream, Stream outStream) {
     // Define the size of the chunk to copy during each iteration (1 KiB)
     const int blockSize = 1024;
     const int bytesToOmit = 16;

     const int buffSize = blockSize + bytesToOmit;

     // Generate working buffers
     byte[] buffer1 = new byte[buffSize];
     byte[] buffer2 = new byte[buffSize];

     // Initialize first iteration
     byte[] curBuffer = buffer1;
     byte[] prevBuffer = null;

     int bytesRead;

     // Attempt to fully fill the buffer
     bytesRead = inStream.Read(curBuffer, 0, buffSize);
     if( bytesRead == buffSize ) {
        // We succesfully retrieved a whole buffer, we will output
        // only [blockSize] bytes, to avoid writing to the last
        // bytes in the buffer in case the remaining 16 bytes happen to 
        // be the last ones
        outStream.Write(curBuffer, 0, blockSize);
     } else {
        // We couldn't retrieve the whole buffer
        int bytesToWrite = bytesRead - bytesToOmit;
        if( bytesToWrite > 0 ) {
           outStream.Write(curBuffer, 0, bytesToWrite);
        }
        // There's no more data to process
        return;
     }

     curBuffer = buffer2;
     prevBuffer = buffer1;

     while( true ) {
        // Attempt again to fully fill the buffer
        bytesRead = inStream.Read(curBuffer, 0, buffSize);
        if( bytesRead == buffSize ) {
           // We retrieved the whole buffer, output first the last 16 
           // bytes of the previous buffer, and output just [blockSize]
           // bytes from the current buffer
           outStream.Write(prevBuffer, blockSize, bytesToOmit);
           outStream.Write(curBuffer, 0, blockSize);
        } else {
           // We could not retrieve a complete buffer 
           if( bytesRead <= bytesToOmit ) {
              // The bytes to output come solely from the previous buffer
              outStream.Write(prevBuffer, blockSize, bytesRead);
           } else {
              // The bytes to output come from the previous buffer and
              // the current buffer
              outStream.Write(prevBuffer, blockSize, bytesToOmit);
              outStream.Write(curBuffer, 0, bytesRead - bytesToOmit);
           }
           break;
        }
        // swap buffers for next iteration
        byte[] swap = prevBuffer;
        prevBuffer = curBuffer;
        curBuffer = swap;
     }
  }

static void Assert(Stream inStream, Stream outStream) {
   // Routine that tests the copy worked as expected
         inStream.Seek(0, SeekOrigin.Begin);
         outStream.Seek(0, SeekOrigin.Begin);
         Debug.Assert(outStream.Length == Math.Max(inStream.Length - bytesToOmit, 0));
         for( int i = 0; i < outStream.Length; i++ ) {
            int byte1 = inStream.ReadByte();
            int byte2 = outStream.ReadByte();
            Debug.Assert(byte1 == byte2);
         }

      }

コードのはるかに簡単な解決策は、バイト レベルで動作するため遅くなりますが、入力ストリームと出力ストリームの間に中間キューを使用することです。このプロセスは、最初に入力ストリームから 16 バイトを読み取り、キューに入れます。次に、残りの入力バイトを反復処理し、入力ストリームから 1 バイトを読み取り、それをキューに入れ、バイトをデキューします。キューから取り出されたバイトは、入力ストリームからのすべてのバイトが処理されるまで、出力ストリームに書き込まれます。不要な 16 バイトは、中間キューに残る必要があります。

お役に立てれば!

=)

于 2013-06-18T23:58:43.833 に答える
0

循環バッファーを使用するのは素晴らしいことのように聞こえますが、.NET には循環バッファー クラスがないため、追加のコードが必要になります。最終的に次のようなアルゴリズムになりましたmap and copy。単純だと思います。ここでは自己記述的であるため、変数名は通常よりも長くなります。

これはバッファを次のように流れます。

[outStream] <== [tailBuf] <== [mainBuf] <== [inStream]

public byte[] CopyStreamExtractLastBytes(Stream inStream, Stream outStream,
                                         int extractByteCount)
{
    //var mainBuf = new byte[1024*4]; // 4K buffer ok for network too
    var mainBuf = new byte[4651]; // nearby prime for testing

    int mainBufValidCount;
    var tailBuf = new byte[extractByteCount];
    int tailBufValidCount = 0;

    while ((mainBufValidCount = inStream.Read(mainBuf, 0, mainBuf.Length)) > 0)
    {
        // Map: how much of what (passthru/tail) lives where (MainBuf/tailBuf)
        // more than tail is passthru
        int totalPassthruCount = Math.Max(0, tailBufValidCount + 
                                    mainBufValidCount - extractByteCount);
        int tailBufPassthruCount = Math.Min(tailBufValidCount, totalPassthruCount);
        int tailBufTailCount = tailBufValidCount - tailBufPassthruCount;
        int mainBufPassthruCount = totalPassthruCount - tailBufPassthruCount;
        int mainBufResidualCount = mainBufValidCount - mainBufPassthruCount;

        // Copy: Passthru must be flushed per FIFO order (tailBuf then mainBuf)
        outStream.Write(tailBuf, 0, tailBufPassthruCount);
        outStream.Write(mainBuf, 0, mainBufPassthruCount);

        // Copy: Now reassemble/compact tail into tailBuf
        var tempResidualBuf = new byte[extractByteCount];
        Array.Copy(tailBuf, tailBufPassthruCount, tempResidualBuf, 0, 
                      tailBufTailCount);
        Array.Copy(mainBuf, mainBufPassthruCount, tempResidualBuf, 
                      tailBufTailCount, mainBufResidualCount);
        tailBufValidCount = tailBufTailCount + mainBufResidualCount;
        tailBuf = tempResidualBuf;
    }
    return tailBuf;
}
于 2013-06-21T20:10:07.393 に答える