seek方法仅适用于filestream实例,需以readwrite模式打开文件;seekorigin决定偏移基准:begin从开头、current从当前位置、end从末尾;读写前须校验position与length避免越界。

Seek 方法必须配合 FileStream 使用
Seek 是 FileStream 类的实例方法,不是 File 或 StreamReader/StreamWriter 的方法。直接用 File.ReadAllBytes 或封装好的高层 API 无法调用 Seek —— 它们内部不暴露底层流位置控制能力。
常见错误是试图对 StreamWriter 调用 Seek,结果编译报错:'StreamWriter' does not contain a definition for 'Seek'。正确做法是显式创建 FileStream,并确保以可读写模式(FileMode.Open + FileAccess.ReadWrite)打开:
using var fs = new FileStream("data.bin", FileMode.Open, FileAccess.ReadWrite);
注意:如果文件只读打开(FileAccess.Read),调用 Seek 虽不报错,但后续 Write 会抛出 NotSupportedException。
SeekOrigin 参数决定偏移基准点
Seek 方法签名是 Seek(long offset, SeekOrigin origin),其中 origin 控制“从哪开始算”:
-
SeekOrigin.Begin:从文件开头(位置 0)开始计算,offset为正表示向后跳,常用于定位到指定字节地址 -
SeekOrigin.Current:从当前读写位置开始计算,offset可正可负,适合前后微调 -
SeekOrigin.End:从文件末尾开始计算,offset通常为负值(如-4表示倒数第 4 字节),注意offset = 0指向末尾之后一个位置(即追加点)
容易踩的坑:用 SeekOrigin.End 时传入正数,会导致位置落在文件数据之外;读写前未检查 Position 是否越界,可能引发 IOException 或静默截断。
读写前务必校验当前位置和剩余长度
随机读写最易出错的环节是“以为能读/写,其实越界了”。例如想从位置 1000 读取 4 字节 int,但文件只有 1002 字节长,实际只能读 2 字节——Read 返回值可能小于预期,不检查就直接解析会出错。
安全做法是:
- 用
fs.Length获取文件总长度 - 用
fs.Position查看当前偏移 - 读之前确认
fs.Position + bytesToRead - 写之前确认
fs.Position + bytesToWrite (若不想扩展文件)或提前用 <code>SetLength扩容
示例:安全读取固定长度结构体
if (fs.Position + sizeof(int) > fs.Length) throw new InvalidOperationException("Not enough data to read int");<br>var buffer = new byte[sizeof(int)];<br>int read = fs.Read(buffer, 0, buffer.Length);<br>if (read != buffer.Length) throw new IOException($"Short read: expected {buffer.Length}, got {read}");<br>int value = BitConverter.ToInt32(buffer, 0);
Seek 后的读写行为受缓冲区影响
默认 FileStream 启用内部缓冲(bufferSize=4096),这会导致两个现象:
-
Seek后立即Read,可能触发整块缓冲区加载,但只返回你需要的部分;多次小Seek+ 小读不会明显变慢 - 若用
Write修改已缓存的数据区域,缓冲区内容会延迟刷新到底层文件,Flush或Dispose才真正落盘
对实时性要求高的场景(如日志修补、内存映射替代方案),可关缓冲:new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None, bufferSize: 1),但 I/O 开销会上升。一般情况保持默认即可。
复杂点在于:Seek 不是原子操作,多线程同时读写同一文件需自行加锁;另外,部分文件系统(如某些网络共享)对随机写支持不佳,Seek 后写入可能比顺序写慢得多——这点容易被忽略。









