C# 用内存映射文件读取大文件(.txt)

 

网上有好多这类的文章,大部分都是用C/C++写的,也有部分C#写的,都思想都是一样的,调用win32 API。

至于什么是内存映射文件,相信还是有好多人不知道是怎么一回事的,我也是偶然看window 核心编程了解到的。

C# 读取大文件的方法也是用的用StreamReader一次读出来,再用MemoryStream放在内存,再用StreamReader一行行的读出来,速度也挺快的,16M的文本大概也就8秒左右,算起来差不多算快了。不过还是不能满足大文件(我没测试)。

) {
this.richTextBox1.AppendText(sr.ReadLine() +"\r\n"); Application.DoEvents(); }
}

 内存映射文件概述
  内存文件映射也是Windows的一种内存管理方法,提供了一个统一的内存管理特征,使应用程序可以通过内存指针对磁盘上的文件进行访问,其过程就如同对加载了文件的内存的访问。通过文件映射这种使磁盘文件的全部或部分内容与进程虚拟地址空间的某个区域建立映射关联的能力,可以直接对被映射的文件进行访问,而不必执行文件I/O操作也无需对文件内容进行缓冲处理。内存文件映射的这种特性是非常适合于用来管理大尺寸文件的。
  在使用内存映射文件进行I/O处理时,系统对数据的传输按页面来进行。至于内部的所有内存页面则是由虚拟内存管理器来负责管理,由其来决定内存页面何时被分页到磁盘,哪些页面应该被释放以便为其它进程提供空闲空间,以及每个进程可以拥有超出实际分配物理内存之外的多少个页面空间等等。由于虚拟内存管理器是以一种统一的方式来处理所有磁盘I/O的(以页面为单位对内存数据进行读写),因此这种优化使其有能力以足够快的速度来处理内存操作。
  使用内存映射文件时所进行的任何实际I/O交互都是在内存中进行并以标准的内存地址形式来访问。磁盘的周期性分页也是由操作系统在后台隐蔽实现的,对应用程序而言是完全透明的。内存映射文件的这种特性在进行大文件的磁盘事务操作时将获得很高的效益。
  需要说明的是,在系统的正常的分页操作过程中,内存映射文件并非一成不变的,它将被定期更新。如果系统要使用的页面目前正被某个内存映射文件所占用,系统将释放此页面,如果页面数据尚未保存,系统将在释放页面之前自动完成页面数据到磁盘的写入。
  对于使用页虚拟存储管理的Windows操作系统,内存映射文件是其内部已有的内存管理组件的一个扩充。由可执行代码页面和数据页面组成的应用程序可根据需要由操作系统来将这些页面换进或换出内存。如果内存中的某个页面不再需要,操作系统将撤消此页面原拥用者对它的控制权,并释放该页面以供其它进程使用。只有在该页面再次成为需求页面时,才会从磁盘上的可执行文件重新读入内存。同样地,当一个进程初始化启动时,内存的页面将用来存储该应用程序的静态、动态数据,一旦对它们的操作被提交,这些页面也将被备份至系统的页面文件,这与可执行文件被用来备份执行代码页面的过程是很类似的。图1展示了代码页面和数据页面在磁盘存储器上的备份过程:

图1 进程的代码页、数据页在磁盘存储器上的备份
  显然,如果可以采取同一种方式来处理代码和数据页面,无疑将会提高程序的执行效率,而内存映射文件的使用恰恰可以满足此需求。

对大文件的管理
  内存映射文件对象在关闭对象之前并没有必要撤销内存映射文件的所有视图。在对象被释放之前,所有的脏页面将自动写入磁盘。通过 CloseHandle()关闭内存映射文件对象,只是释放该对象,如果内存映射文件代表的是磁盘文件,那么还需要调用标准文件I/O函数来将其关闭。在处理大文件处理时,内存映射文件将表示出卓越的优势,只需要消耗极少的物理资源,对系统的影响微乎其微。下面先给出内存映射文件的一般编程流程框图:

图2 使用内存映射文件的一般流程
  而在某些特殊行业,经常要面对十几GB乃至几十GB容量的巨型文件,而一个32位进程所拥有的虚拟地址空间只有232 = 4GB,显然不能一次将文件映像全部映射进来。对于这种情况只能依次将大文件的各个部分映射到进程中的一个较小的地址空间。这需要对上面的一般流程进行适当的更改:
  1)映射文件开头的映像。
  2)对该映像进行访问。
  3)取消此映像
  4)映射一个从文件中的一个更深的位移开始的新映像。
  5)重复步骤2,直到访问完全部的文件数据。

下面是用C#写的代码,大部分代码转自网上,自己在原来的基础上改了一改。

C#内存映射文件代码

, (int)blockBytes);
//用循环太慢了,文件有几M的时候就慢的要死,还是用上面的方法直接 //for (uint i = 0; i < dwBlockBytes; i++) //{ // byte vTemp = Marshal.ReadByte((IntPtr)((int)lpbMapAddress + i)); // temp[i] = vTemp; //} //此时用ASCII解码比较快,但有中文会有乱码,用gb2312即ANSI编码也比较快,16M的文件大概4秒就读出来了 //但用unicode解码,文件大的时候会非常慢,会现卡死的状态,不知道为什么? //ASCIIEncoding encoding = new ASCIIEncoding(); //System.Text.UnicodeEncoding encoding = new UnicodeEncoding(); //sb.Append(encoding.GetString(temp)); sb.Append(System.Text.Encoding.GetEncoding("gb2312").GetString(temp)); // 撤消文件映像 UnmapViewOfFile(lpbMapAddress); // 修正参数 fileOffset += blockBytes; fileSize -= blockBytes; }
//close file mapping handle CloseHandle(mappingFileHandle); } } return sb; }
} }

经过测试16M的文本4秒可以读出来。

现在是有两个问题还没有解决:

1.就是编码的问题,用Unicode解码的时候,文件大会很慢,而用ANSI和ASCII就很快。不知道为什么,望知情者告之。

2.怎么知道文件的编码是什么?用win32 IsTestUnicode只能判断两种,而且还不保证是对。

c# 读取内存的更多相关文章

  1. 基于最简单的FFmpeg采样读取内存读写:存储转

    ===================================================== 基于最简单的FFmpeg样品系列读写内存列表: 最简单的基于FFmpeg的内存读写的样例:内 ...

  2. MFC中psz_data无法读取内存的错误

    1.项目 ->属性->C/C++ -->预编译头文件,改为“不适用预编译头” 出现ATL::CSimplestring错误,psz_data无法读取内存 2.修改如下:鼠标右击 项目 ...

  3. 【基础知识】CPU 是如何工作的 |CPU 通过总线读取内存的工作方式

    一.简单cpu  是如何工作 方式讲解 CPU 的根本任务就是执行指令,对计算机来说最终都是一串由 0 和 1 组成的序列.CPU 从逻辑上可以划分成 3 个模块,分别是控制单元.运算单元和存储单元 ...

  4. 基于最简单的FFmpeg采样读取内存读写:内存玩家

    ===================================================== 基于最简单的FFmpeg样品系列读写内存列表: 最简单的基于FFmpeg的内存读写的样例:内 ...

  5. python与C交互中传入与读取内存空间

    使用用python调用c代码中,从外部传入一个固定大小的内存空间,这段内存需要是可写的 首先看下c中的函数 typedef struct ModelData { unsigned int model_ ...

  6. Mysql遍历大表(Mysql大量数据读取内存溢出的解决方法)

    mysql jdbc默认把select的所有结果全部取回,放到内存中,如果是要遍历很大的表,则可能把内存撑爆. 一种办法是:用limit,offset,但这样你会发现取数据的越来越慢,原因是设置了of ...

  7. 026 Android 带不同类型条目的listview(纯文本类型的条目,图片+文字类型的条目)+读取内存空间、手机进程信息+常驻悬浮框

    1.目标效果 带不同类型条目的listview(纯文本类型的条目,图片+文字类型的条目)+常驻悬浮框 2.页面布局文件 (1)activity_process_manager.xml <?xml ...

  8. BitmapImage 读取内存流和显示图片

    FileStream filestream = File.OpenRead(@"C:\Users\Administrator\Desktop\queryHeaderImg.png" ...

  9. GDI+ Image 读取内存二进制流显示图片

    int iBmpSize = cd.nTotleLen; HGLOBAL hMemBmp = GlobalAlloc(GMEM_FIXED, iBmpSize); IStream* pStmBmp = ...

随机推荐

  1. 新版VS-code如何自动换行?

    文件  -> 首选项 -> 设置 -> 编辑器 找到 // 控制折行方式.可以选择: - "off" (禁用折行), - "on" (视区折行 ...

  2. 【收藏】下载Chrome商店插件的方法,万恶的gwd

    以下是下载离线插件包的方法: 第一步: 每个Google Chrome扩展都有一个固定的ID,例如https://chrome.google.com/webstore/detail/bfbmjmiod ...

  3. 【HDOJ6342】Expression in Memories(模拟)

    题意: 给定一个由0123456789+* ?组成的表达式,其中?可以被改为任意其它字符,问修改问号后是否有方案使得表达式合法 len<=5e2,sumlen<=1e5 思路: #incl ...

  4. CSS属性操作一

    CSS属性操作 一.CSS text 1.文本颜色:color 颜色属性被用来设置文字的颜色.颜色是通过CSS最经常的指定: • 十六进制值 - 如: #FF0000 • 一个RGB值 - 如: RG ...

  5. [Bzoj1034][ZJOJ2008]泡泡堂BNB(贪心)

    1034: [ZJOI2008]泡泡堂BNB Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3482  Solved: 1776[Submit][St ...

  6. win7 休眠功能的开启与关闭

    1. 打开控制面板得下面目录,编辑计划设置

  7. C# Queue与RabbitMQ的爱恨情仇(文末附源码):Q与MQ消息队列简单应用(二)

    上一章我们讲了队列( Queue),这一章我们讲Message Queue消息队列,简称MQ. 定义: MQ是MessageQueue,消息队列的简称(是流行的开源消息队列系统,利用erlang语言开 ...

  8. Scala入门到精通——第十六节 泛型与注解

    本节主要内容 泛型(Generic Type)简单介绍 注解(Annotation)简单介绍 注解经常使用场景 1. 泛型(Generic Type)简单介绍 泛型用于指定方法或类能够接受随意类型參数 ...

  9. [NPM] Create a new project using the npm init <initializer> command

    Historically, the npm init command was solely use to create a new package.json file. However, as of ...

  10. 万恶之源:C语言中的隐式函数声明

    1 什么是C语言的隐式函数声明 在C语言中,函数在调用前不一定非要声明.如果没有声明,那么编译器会自己主动依照一种隐式声明的规则,为调用函数的C代码产生汇编代码.以下是一个样例: int main(i ...