C# dotnet 使用 FileStream 随机文件读写
本文说的随机文件读写的随机的反义词是顺序,这里的随机文件读写对应顺序文件读写。表示文件可以不按照顺序进行读写
进行文件读写的时候,基本上读是几乎不存在问题的,而写的话就稍微坑了一点,在 dotnet 里面默认没有提供 RandomAccessStream 类,这个 RandomAccessStream 类仅在 UWP 中可以使用
如果在不引用 UWP 的 WPF 里面,或者在 ASP.NET Core 以及 Xamarin 里面,也可以通过 FileStream 的 Seek 方法做到进行随机的读写
在随机读写文件的时候使用 FileStream 的 Seek 方法设置当前的文件 Stream 所在的点,此时就可以从 Stream 的这个点开始进行读写。在 Stream 的 Seek 方法会在 FileStream.Windows.cs 调用 SeekCore 方法,在 SeekCore 会调用 Kernel32.SetFilePointerEx 的方法设置到文件的读写
此时使用 Position 属性也能完成,在 FileStream.cs 里面可以看到 Position 的 Set 方法本质也是调用 Seek 方法
public override long Position
{
get {/*忽略代码*/}
set
{
// 忽略代码
Seek(value, SeekOrigin.Begin);
}
}
比较推荐使用 Seek 的方法,因此这个方法功能比较强大,可以设置相对或者从前开始等
大概的做法是如移动到某个字节处开始读写,可以使用如下代码
private async Task WriteFile(long fileStartPoint, byte[] data, int dataLength)
{
Stream.Seek(fileStartPoint, SeekOrigin.Begin);
await Stream.WriteAsync(data, offset: 0, dataLength);
}
注意这里的 WriteAsync 使用的第二个参数 offset
指的是第一个参数 byte[]
的偏移而不是写入到 Stream 的偏移。通过 Seek 的方法就能做到让文件支持进行随机读写
另外,如果想要比较大的提升随机文件读写性能,我推荐在知道文件长度的时候通过 SetLength 方法设置文件长度,这样能减少文件碎片分配
如果需要进行多线程读写,此时读可以采用创建多个 FileStream 的方法,注意设置读共享。但如果存在多线程写入,我推荐是使用一个 FileStream 然后其他多个线程委托到一个线程里面进行写入,而不是多个线程同时写入。原因是多个线程同时写入的时候冲突不好处理,加上文件写入有磁盘延迟,此时的写入特别是有长度变化的时候会写出空值
我通过 AsyncQueue 做到多个线程不断写入队列,而一个线程不断从队列取出待写入的数据,写入到文件。这样做的优势在于能做到在一个线程里面写入文件,而其他线程只是委托这个写入文件线程写入,其他线程不访问文件
这部分多线程进行文件随机写入代码放在 github 欢迎小伙伴访问,代码放在 RandomFileWriter.cs 文件
更多 dotnet 底层源代码请看 官方开源代码 本文用到的代码放在 srclibrariesSystem.Private.CoreLibsrcSystemIOFileStream.cs
和 srclibrariesSystem.Private.CoreLibsrcSystemIOFileStream.Windows.cs
文件
那么文件随机读写的应用是什么?
可以用在一些业务上,这些业务不需要按照顺序读写文件。例如文件的配置的读写等
文件随机读写可以用在文件配置读写上面,例如我知道文件的数据结构,我的某个数据放在第100个字节到第200个字节间,此时我需要读取修改这个数据的内容,我不需要完全去读取前100个字节的内容,我可以直接使用随机读写的方法读取第100个字节到第200个字节的内容。而写入也同理,我不需要从第0个字节开始写入,我可以从第100个字节开始写入。这样能提升一些读写性能
本文会经常更新,请阅读原文: https://blog.lindexi.com/post/C-dotnet-%E4%BD%BF%E7%94%A8-FileStream-%E9%9A%8F%E6%9C%BA%E6%96%87%E4%BB%B6%E8%AF%BB%E5%86%99.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
- RabbitMQ(三) ——发布订阅
- RabbitMQ(四) ——路由
- RabbitMQ(六) ——RPC模式
- VFS四大对象之一 struct super_block
- 高性能网站架构方案(二)——优化网站响应时间
- VFS四大对象之二 struct inode
- Ubuntu 安装 Redis
- 高性能网站架构方案(三) ——Varnish加速与Gearman任务分发
- mysql 性能优化方案 (转)
- 《Redis设计与实现》读书笔记(一)——简单动态字符串(SDS)
- Comet:基于 HTTP 长连接的“服务器推”技术
- 编码修炼 | 快速了解Scala技术栈
- VFS四大对象之三 struct dentry
- PHP 排序算法实现讲解
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 使用pygame实现垃圾分类小游戏功能(已获校级二等奖)
- Linux 系统下安装JDK1.8的教程详解
- php学习笔记之字符串常见操作总结
- Laravel5.1 框架模型工厂ModelFactory用法实例分析
- 关于AIX挂载NFS写入效率低效的解决办法
- Linux系统下部署项目的设置办法
- PHP利用缓存处理用户注册时的邮箱验证,成功后用户数据存入数据库操作示例
- ubantu 16.4下Hadoop完全分布式搭建实战教程
- Laravel5.1 框架模型查询作用域定义与用法实例分析
- 整理Linux中字符串的相关操作技巧
- Laravel5.1 框架Middleware中间件基本用法实例分析
- Linux基础命令之mktemp详解
- TP3.2.3框架使用CKeditor编辑器在页面中上传图片的方法分析
- Laravel5.1 框架模型一对一关系实现与使用方法实例分析
- Linux中怎么查看已挂载的文件系统类型详解