// 不推薦:用例外處理判斷 EOF
var fs = File.OpenRead(path);
var br = new BinaryReader(fs);
try
{
while (true)
{
var b = br.ReadByte(); // 結尾拋出 EndOfStreamException
// 處理 b
}
}
catch (EndOfStreamException)
{
// 正常結束
}
finally { br.Close(); }
缺點:例外處理成本高、語意不清、掩蓋真正錯誤。
using (var fs = File.OpenRead(path))
using (var br = new BinaryReader(fs))
{
while (br.BaseStream.Position < br.BaseStream.Length)
{
var b = br.ReadByte();
// 或讀取結構化資料
// var i = br.ReadInt32();
// var s = br.ReadString();
}
}
Position 為當前指標,Length 為總長度
using (var fs = File.OpenRead(path))
using (var br = new BinaryReader(fs))
{
while (br.PeekChar() > -1)
{
var b = br.ReadByte();
// 處理
}
}
PeekChar() 回傳下一個字元(不消耗),結尾回傳 -1BinaryReader 內部緩衝可能導致 PeekChar 與實際讀取位置不一致,建議搭配 ReadByte 使用
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct FileHeader
{
public uint Magic; // 4 bytes
public ushort Version; // 2 bytes
public ushort Count; // 2 bytes
// 總計 8 bytes
}
public static FileHeader ReadHeader(string path)
{
using (var fs = File.OpenRead(path))
using (var br = new BinaryReader(fs))
{
if (fs.Length < 8) throw new InvalidDataException("檔案太小");
var header = new FileHeader
{
Magic = br.ReadUInt32(),
Version = br.ReadUInt16(),
Count = br.ReadUInt16()
};
return header;
}
}
// 記憶體對應檔案(適合超大檔、隨機存取)
using (var mmf = MemoryMappedFile.CreateFromFile(path, FileMode.Open, null, 0, MemoryMappedFileAccess.Read))
using (var accessor = mmf.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read))
{
var header = accessor.Read(0);
// 隨機讀取第 N 筆記錄
long offset = 8 + N * RecordSize;
var record = accessor.Read(offset);
}
// 無法使用 Position/Length/PeekChar
// 必須依協定讀取:長度前綴、分隔符、或固定大小
using (var ns = new NetworkStream(socket))
using (var br = new BinaryReader(ns, Encoding.UTF8, leaveOpen: true))
{
while (true)
{
// 範例:長度前綴協定
if (!TryReadInt32(br, out int len)) break; // 連線關閉或錯誤
if (len < 0 || len > MaxMessageSize) throw new ProtocolException();
var data = br.ReadBytes(len);
if (data.Length != len) throw new EndOfStreamException();
ProcessMessage(data);
}
}
bool TryReadInt32(BinaryReader br, out int value)
{
var buffer = new byte[4];
int totalRead = 0;
while (totalRead < 4)
{
int read = br.BaseStream.Read(buffer, totalRead, 4 - totalRead);
if (read == 0) { value = 0; return false; } // EOF
totalRead += read;
}
value = BitConverter.ToInt32(buffer, 0);
return true;
}
| 資料流類型 | 判斷 EOF 方法 | 備註 |
|---|---|---|
| FileStream, MemoryStream | Position < Length 或 PeekChar() > -1 | 可搜尋,效能最佳 |
| MemoryMappedFile | 直接依 offset 讀取,無需迴圈判斷 EOF | 隨機存取、大檔案首選 |
| NetworkStream, PipeStream | 協定層面處理(長度前綴/分隔符) | 不可搜尋,不可用 Position/Length |
| GZipStream, CryptoStream | 嘗試讀取,回傳 0 表示結束 | ReadByte() == -1 或 Read(buffer,...) == 0 |
核心原則:依資料流特性選擇正確的 EOF 判斷方式,拒絕用例外控制流程,讓程式碼語意清晰、效能最佳。