2012-02-06 63 views
3

我想分割一個字節流成大小不斷增加的塊。如何實現一個懶惰的流塊枚舉器?

源流包含未知數量的字節,且讀取起來非常昂貴。枚舉器的輸出應該是大小遞增的字節數組,從8KB開始到1MB。

這很簡單,只需讀取整個流,將其存儲在一個數組中,然後取出相關的部分。然而,由於流可能非常大,因此立即閱讀是不可行的。此外,儘管性能不是主要關注的問題,但保持系統負載非常低很重要。

在實現這個過程中,我注意到保持代碼的簡潔性和維護性相對困難。有幾個流相關的問題也要記住(例如,Stream.Read可能不會填充緩衝區,即使它成功了)。

我沒有找到任何有助於我的案例的現有類,也不能在網上找到關閉的東西。你將如何實施這樣的課程?

+0

你會遍歷它不止一次?迭代器能夠返回流還是需要返回byte []?如果它是流,它會在你繼續前完全閱讀嗎?如果是這樣的話,編寫一個迭代器很容易,只需使用一個小緩衝區就可以將源碼流拆分爲更小的碼流。或者,你是否知道一開始塊的大小,即是否可以分配一個緩衝區並一次讀取整個塊,或者你是否需要通過它來翻頁找到結束標記? – Rup 2012-02-06 15:50:42

+0

@Rup:只迭代一次。必須是字節[]。整個源碼流太大,無法完全讀取,但各個塊將適合內存。所有塊的大小和位置是已知的,但源流的實際長度是未知的(最終塊可能小於其假定的大小)。 – mafu 2012-02-06 16:02:04

回答

3
public IEnumerable<BufferWrapper> getBytes(Stream stream) 
{ 
    List<int> bufferSizes = new List<int>() { 8192, 65536, 220160, 1048576 }; 
    int count = 0; 
    int bufferSizePostion = 0; 
    byte[] buffer = new byte[bufferSizes[0]]; 
    bool done = false; 
    while (!done) 
    { 
     BufferWrapper nextResult = new BufferWrapper(); 
     nextResult.bytesRead = stream.Read(buffer, 0, buffer.Length); 
     nextResult.buffer = buffer; 
     done = nextResult.bytesRead == 0; 
     if (!done) 
     { 
      yield return nextResult; 
      count++; 
      if (count > 10 && bufferSizePostion < bufferSizes.Count) 
      { 
       count = 0; 
       bufferSizePostion++; 
       buffer = new byte[bufferSizes[bufferSizePostion]]; 
      } 
     } 
    } 
} 

public class BufferWrapper 
{ 
    public byte[] buffer { get; set; } 
    public int bytesRead { get; set; } 
} 

顯然,何時向上移動緩衝區大小以及如何選擇大小的邏輯可能會改變。

有人也可能找到更好的方法來處理最後發送的緩衝區,因爲這不是最有效的方法。

+0

剛纔我發現你可以爲一個字節數組創建自己的包裝,該數組既可以存儲字節數組又可以存儲實際數據的實際長度,並且每次都會返回。這意味着你不需要處理最後一個局部緩衝區的數組副本。 – Servy 2012-02-06 16:03:03

+0

stream.Read存在一個錯誤:通過返回0來讀取只讀信號EOF(而不是bytesRead mafu 2012-02-06 16:05:50

+0

+1,但爲什麼複製緩衝區?爲什麼不只是返回你讀的緩衝區並且總是分配一個新緩衝區? – Rup 2012-02-06 16:06:20

2

僅供參考,我目前使用,已經與改進按照答案通過@Servy

實施
private const int InitialBlockSize = 8 * 1024; 
private const int MaximumBlockSize = 1024 * 1024; 

private Stream _Stream; 
private int _Size = InitialBlockSize; 

public byte[] Current 
{ 
    get; 
    private set; 
} 

public bool MoveNext() 
{ 
    if (_Size < 0) { 
     return false; 
    } 

    var buf = new byte[_Size]; 
    int count = 0; 

    while (count < _Size) { 
     int read = _Stream.Read (buf, count, _Size - count); 

     if (read == 0) { 
      break; 
     } 

     count += read; 
    } 

    if (count == _Size) { 
     Current = buf; 
     if (_Size <= MaximumBlockSize/2) { 
      _Size *= 2; 
     } 
    } 
    else { 
     Current = new byte[count]; 
     Array.Copy (buf, Current, count); 
     _Size = -1; 
    } 

    return true; 
}