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 ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
- linux学习第六十篇:Linux监控平台介绍,zabbix监控介绍,安装zabbix,忘记Admin密码如何做
- 支持向量机及Python代码实现
- 【技术博客】Android自定义Lint实践
- UC浏览器皮肤的那个坑
- 文本溢出-超出文本显示为省略号
- 征信区块链解决方案探索(Hyperledger)
- 概率论12 矩与矩生成函数
- Python的hasattr() getattr() setattr() 函数使用方法详解
- 查找字符串中出现最多的字符
- C++工程中常用的宏定义(#define)
- 面向对象系列讲解—认识对象
- 基于 KIF 的 iOS UI 自动化测试和持续集成
- 面向对象系列讲解—面向对象的含义&工厂模式
- 庖丁解牛看委托和事件(续)
- 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 数组属性和方法
- one-hot encoding不是万能的,这些分类变量编码方法你值得拥有
- 【5分钟玩转Lighthouse】爬取JavaScript动态渲染页面
- 科技爱好者周刊(第 127 期):未来人人开发软件,几乎没人编码
- mac快捷键
- 浅谈Kotlin的Checked Exception机制
- 自研网关:特殊URL功能的开发
- centos7多网卡配置
- vuecli3 build之后静态文件出现404
- Muti-Similarity Loss:考虑了batch中整体距离分布的对比损失函数
- AkShare-中国宏观-社会消费品零售总额
- AkShare-中国宏观-存款准备金率
- AkShare-中国宏观-消费者信心指数
- AkShare-期货数据-COMEX库存数据
- ESP8266和ESP32配置(需使用ROS1和ROS2)
- SpringBoot+Gradle+ MyBatisPlus3.x搭建企业级的后台分离框架