Seek方法需配合支持定位的可读写流使用,检查CanSeek为true后再调用;避免在StreamReader/StreamWriter中混用Seek;大文件优先用MemoryMappedFile替代。

Seek 方法必须配合可读写流使用
直接对 FileStream 调用 Seek 失败,大概率是因为流没以支持定位的模式打开。.NET 中只有打开时指定 FileAccess.ReadWrite 或对应权限,且底层文件句柄支持随机访问(如磁盘文件),CanSeek 才为 true。只用 FileAccess.Read 打开的流,即使物理上可定位,CanSeek 也可能返回 false —— 这是 .NET 的安全限制,不是 bug。
实操建议:
- 用
new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.None)显式声明读写权限 - 调用前务必检查
stream.CanSeek,避免运行时报NotSupportedException - 网络流、加密流(如
CryptoStream)、压缩流(如GZipStream)通常不支持Seek,别强行调用
SeekOrigin 的三个取值含义和常见误用
SeekOrigin.Begin、SeekOrigin.Current、SeekOrigin.End 看似简单,但容易在边界计算出错。尤其是 SeekOrigin.End:它把偏移量当作“从末尾往前数”,所以要定位到倒数第 10 字节,得传 -10,而不是 10;若传正数会抛 IOException(“试图访问无效位置”)。
实操建议:
- 用
stream.Length获取总长度,再结合SeekOrigin.Begin计算更直观,比如跳到第 1024 字节:stream.Seek(1024, SeekOrigin.Begin) - 修改当前位置后立即读写,别依赖“上次位置”,
Seek不会自动刷新缓冲区,后续Read/Write才真正触发 I/O - 向文件末尾追加数据时,不要用
SeekOrigin.End再写,直接用FileMode.Append更安全
Seek 后读写中文或 UTF-8 字符串容易乱码
这是最隐蔽的问题:Seek 操作的是字节偏移,而字符串编码(如 UTF-8)中一个汉字占 2~4 字节。如果用 StreamReader 读文本,它内部有解码缓冲区,Seek 直接跳到中间字节会导致后续 ReadLine 解码失败,抛 ArgumentException(“数据无效”)。
一套面向小企业用户的企业网站程序!功能简单,操作简单。实现了小企业网站的很多实用的功能,如文章新闻模块、图片展示、产品列表以及小型的下载功能,还同时增加了邮件订阅等相应模块。公告,友情链接等这些通用功能本程序也同样都集成了!同时本程序引入了模块功能,只要在系统默认模板上创建模块,可以在任何一个语言环境(或任意风格)的适当位置进行使用!
实操建议:
- 纯文本场景下,避免混合使用
Seek和StreamReader/StreamWriter;改用FileStream+Encoding.GetBytes/GetString手动处理字节 - 若必须按行操作,先用
stream.Position记录每行起始字节位置,Seek时只跳到这些已知合法位置 - UTF-8 BOM(
EF BB BF)占 3 字节,开头Seek(0, ...)是安全的,但Seek(1, ...)就可能卡在 BOM 中间
大文件 Seek 性能与内存映射替代方案
对 GB 级文件反复 Seek + 小块读写,性能可能比顺序读还差——因为每次 Seek 触发磁盘寻道,机械硬盘尤其明显。而且 FileStream 默认缓冲区 4KB,小偏移频繁切换位置会让缓存失效。
实操建议:
- 优先用
MemoryMappedFile处理超大文件随机访问,它把文件映射到虚拟内存,Seek变成指针运算,无 I/O 开销 - 若坚持用
FileStream,增大缓冲区(如 64KB),并尽量批量读写,减少Seek次数 - 注意:32 位进程下
Position是long,但某些旧系统 API 对 >2GB 文件支持不稳,测试时务必覆盖边界值
真正麻烦的从来不是 Seek 本身,而是你假设“文件是个字节数组”却忘了编码、缓冲、硬件寻道这些现实约束。









