C#文件随机读写 C#如何使用Seek方法在文件任意位置进行读写

来源:这里教程网 时间:2026-02-21 17:42:58 作者:

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
后写入可能比顺序写慢得多——这点容易被忽略。

相关推荐

热文推荐