1.讀取到內存中;css
2.分塊讀取;html
3.採用內存映射技術。android
此種方式比較適合小文件,能夠經過文件流的方式直接讀取到內存中進行處理。程序員
當文件很大時(特別是文件大小大於內存大小),讀取到內存中就很不合理。這種時候,咱們能夠將文件進行分塊,而後進行分塊讀取。sql
1 /// <summary> 2 /// 讀取大文件方法 3 /// </summary> 4 /// <param name="initialPath">原文件絕對地址</param> 5 /// <param name="aimPath">新文件絕對地址</param> 6 private static void CopyFile(string initialPath, string aimPath) 7 { 8 //1,建立一個讀取文件的文件流 9 using (FileStream fsRead = new FileStream(initialPath, FileMode.Open)) 10 { 11 //4,建立一個寫文件的文件流 12 using (FileStream fsWrite = new FileStream(aimPath, FileMode.Create)) 13 { 14 //2,創建緩衝區 15 byte[] eByte = new byte[1024 * 1024 * 10];//每次讀取的大小 16 while (true) 17 { 18 int r = fsRead.Read(eByte, 0, eByte.Length); 19 if (r <= 0) 20 { 21 break; 22 } 23 //3,r表示向流中寫入,本次實際讀取到文件的大小 24 fsWrite.Write(eByte, 0, r); 25 } 26 27 28 } 29 } 30 }
此時代碼僅僅只是簡單的讀取,沒法直接對文件的內容進行分析。好比日誌文件咱們不只須要讀取,並且還須要對其進行分析,咱們能夠根據文件中特定字符進行分塊處理。特定字符須要根據讀取文件進行分析,好比'\n':可將文件分紅10塊,分紅10塊的時候,一定出現,一段被截斷的狀況,針對這種狀況,可採用一次初查定位,根據10個位點,倒着查出'\n'(特定字符)符號,查到了這個符號,那麼,'\n'以前(包括'\n')都是完整的段數,這樣就能準確的放置出10個位置,而後用Stream中的Postion定位,就能夠分出10塊。windows
string path = @"C:\work\project_log.sql"; byte[] arr = new byte[1024*4]; // 要讀取的字節數 var start = 0; using (var fs = File.OpenRead(path)) { // 讀取大文件的關鍵在這裏 fs.Position = 5418579000; fs.Read(arr, start, arr.Length); } var str = Encoding.UTF8.GetString(arr); Console.WriteLine(str);
using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.IO; namespace CommonLib.Threading.IO.ReadFile { /// <summary> /// 多線程文件讀取器 /// </summary> public abstract class FileReader { private string filePath; private List<FileReadPoint> readPoint=new List<FileReadPoint>(); private bool isStart; private int threadCompleteCount; public event EventHandler FileReadEnd;//文件讀取完成 public bool IsStart { get { return isStart; } } public string FilePath { get { return filePath; } } private FileReader() { } public FileReader(string filePath) { this.filePath = filePath; } /// <summary> /// 獲取讀取文件的起始點和結束點 /// 文件起始點會在參數point中給出 /// </summary> /// <param name="point">讀取文件的起始點和結束點</param> /// <param name="stream">文件流</param> /// <param name="length">文件長度</param> protected abstract void GetPoint(FileReadPoint point,FileStream stream,long length); /// <summary> /// 設置文件讀取起始點 /// </summary> /// <param name="stream"></param> /// <returns></returns> protected virtual int SetStartPoint(FileStream stream) { return 0; } /// <summary> /// 對已用多線程分塊讀取的文件作的處理 /// </summary> /// <param name="threadStream"></param> protected abstract void DoFileRead(ThreadStream threadStream); /// <summary> /// 初始化分塊讀取文件的點 /// </summary> /// <returns></returns> public bool Create() { FileInfo fileInfo = new FileInfo(filePath); fileInfo.Refresh(); if (fileInfo.Exists) { filePath = fileInfo.FullName; using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) { if (readPoint.Count != 0) { readPoint.Clear(); } long startPoint = SetStartPoint(stream); long length = stream.Length; while (startPoint < length) { stream.Position = startPoint; FileReadPoint fPoint = new FileReadPoint(); fPoint.StartPoint = startPoint; GetPoint(fPoint, stream, length); if (fPoint.StartPoint + fPoint.ReadCount > length) { fPoint.ReadCount = length - fPoint.StartPoint; } readPoint.Add(fPoint); startPoint = fPoint.StartPoint + fPoint.ReadCount; } } return true; } else { return false; } } /// <summary> /// 啓動多線程文件讀取 /// </summary> public void StartRead() { if (!isStart) { threadCompleteCount = 0; foreach (FileReadPoint fp in readPoint) { Thread thread = new Thread(OnReadFile); thread.IsBackground = true; thread.SetApartmentState(ApartmentState.MTA); thread.Start(fp); } isStart = true; } } [MTAThread()] private void OnReadFile(object obj) { FileReadPoint fp = obj as FileReadPoint; if (fp != null) { using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) { stream.Position = fp.StartPoint; ThreadStream threadStream = new ThreadStream(stream, fp); DoFileRead(threadStream); } } if (FileReadEnd != null) { lock (readPoint) { threadCompleteCount++; if (threadCompleteCount == readPoint.Count) { FileReadEnd(this, new EventArgs()); } } } } } public class FileReadPoint { private long startPoint = 0L; public long StartPoint { get { return startPoint; } set { startPoint = value; } } private long readCount = 1L; public long ReadCount { get { return readCount; } set { if (value >= 1) { readCount = value;//readCount必須大於1 } } } } public sealed class ThreadStream { private int MAXBLOCK = 1024 * 1024 * 4; private FileStream fileStream; private FileReadPoint fPoint; private long currentCount = 0L; public FileReadPoint FPoint { get { return fPoint; } } private ThreadStream() { } public ThreadStream(FileStream stream, FileReadPoint point) { this.fileStream = stream; this.fPoint = point; } /// <summary> /// 讀取剩餘的全部字節 /// </summary> /// <returns></returns> public byte[] ReadAll() { if (currentCount < fPoint.ReadCount) { long lastCount = fPoint.ReadCount - currentCount; byte[] data = new byte[lastCount]; long currentDataIndex = 0L; while (lastCount > MAXBLOCK) { AddData(MAXBLOCK,data, currentDataIndex); lastCount = lastCount - MAXBLOCK; currentDataIndex += MAXBLOCK; } if (lastCount > 0) { AddData((int)lastCount, data, currentDataIndex); } currentCount = fPoint.ReadCount; return data; } else { return null; } } /// <summary> /// 分塊讀取字節 /// </summary> /// <param name="block"></param> /// <returns></returns> public byte[] Read(int block) { if (currentCount < fPoint.ReadCount) { int currentBlock = block; if (currentCount + block > fPoint.ReadCount) { currentBlock = (int)(fPoint.ReadCount - currentCount); } byte[] data = new byte[currentBlock]; fileStream.Read(data, 0, data.Length); currentCount += currentBlock; return data; } else { return null; } } private void AddData(int block,byte[] data, long currentDataIndex) { byte[] cutData = Read(block); Array.Copy(cutData, 0, data, currentDataIndex, cutData.Length); } } }
內存映射文件是利用虛擬內存把文件映射到進程的地址空間中去,在此以後進程操做文件,就像操做進程空間裏的地址同樣了,好比使用c語言的 memcpy等內存操做的函數。這種方法可以很好的應用在須要頻繁處理一個文件或者是一個大文件的場合,這種方式處理IO效率比普通IO效率要高。數組
內存映射文件包含虛擬內存中文件的內容。 藉助文件和內存空間之間的這種映射,應用(包括多個進程)能夠直接對內存執行讀取和寫入操做,從而修改文件。 自 .NET Framework 4 起,可使用託管代碼訪問內存映射文件,就像本機 Windows 函數訪問內存映射文件同樣。緩存
內存映射文件分爲兩種類型:多線程
1.持久化內存映射文件app
持久化文件是與磁盤上的源文件相關聯的內存映射文件。 當最後一個進程處理完文件時,數據保存到磁盤上的源文件中。 此類內存映射文件適用於處理很是大的源文件。
2.非持久化內存映射文件
非持久化文件是不與磁盤上的文件相關聯的內存映射文件。 當最後一個進程處理完文件時,數據會丟失,且文件被垃圾回收器回收。 此類文件適合建立共享內存,以進行進程內通訊 (IPC)。
詳情可看:https://docs.microsoft.com/zh-cn/dotnet/standard/io/memory-mapped-files
using System; using System.IO; using System.IO.MemoryMappedFiles; using System.Text; namespace ConsoleDemo { class Program { private const string TXT_FILE_PATH = @"E:\work\超大文本文件讀取\Filea.txt"; private const string SPLIT_VARCHAR = "囧"; private const char SPLIT_CHAR = '囧'; private static long FILE_SIZE = 0; static void Main(string[] args) { long ttargetRowNum = 10000000; DateTime beginTime = DateTime.Now; string line = CreateMemoryMapFile(ttargetRowNum); double totalSeconds = DateTime.Now.Subtract(beginTime).TotalSeconds; Console.WriteLine(line); Console.WriteLine(string.Format("查找第{0}行,共耗時:{1}s", ttargetRowNum, totalSeconds)); Console.ReadLine(); } /// <summary> /// 建立內存映射文件 /// </summary> private static string CreateMemoryMapFile(long ttargetRowNum) { string line = string.Empty; using (FileStream fs = new FileStream(TXT_FILE_PATH, FileMode.Open, FileAccess.ReadWrite)) { long targetRowNum = ttargetRowNum + 1;//目標行 long curRowNum = 1;//當前行 FILE_SIZE = fs.Length; using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fs, "test", fs.Length, MemoryMappedFileAccess.ReadWrite, null, HandleInheritability.None, false)) { long offset = 0; //int limit = 250; int limit = 200; try { StringBuilder sbDefineRowLine = new StringBuilder(); do { long remaining = fs.Length - offset; using (MemoryMappedViewStream mmStream = mmf.CreateViewStream(offset, remaining > limit ? limit : remaining)) //using (MemoryMappedViewStream mmStream = mmf.CreateViewStream(offset, remaining)) { offset += limit; using (StreamReader sr = new StreamReader(mmStream)) { //string ss = sr.ReadToEnd().ToString().Replace("\n", "囧").Replace(Environment.NewLine, "囧"); string ss = sr.ReadToEnd().ToString().Replace("\n", SPLIT_VARCHAR).Replace(Environment.NewLine, SPLIT_VARCHAR); if (curRowNum <= targetRowNum) { if (curRowNum < targetRowNum) { string s = sbDefineRowLine.ToString(); int pos = s.LastIndexOf(SPLIT_CHAR); if (pos > 0) sbDefineRowLine.Remove(0, pos); } else { line = sbDefineRowLine.ToString(); return line; } if (ss.Contains(SPLIT_VARCHAR)) { curRowNum += GetNewLineNumsOfStr(ss); sbDefineRowLine.Append(ss); } else { sbDefineRowLine.Append(ss); } } sr.Dispose(); } mmStream.Dispose(); } } while (offset < fs.Length); } catch (Exception e) { Console.WriteLine(e.Message); } return line; } } } private static long GetNewLineNumsOfStr(string s) { string[] _lst = s.Split(SPLIT_CHAR); return _lst.Length - 1; } } }
測試截圖:
下面的示例爲極大文件的一部分建立內存映射視圖,並控制其中一部分。
using System; using System.IO; using System.IO.MemoryMappedFiles; using System.Runtime.InteropServices; class Program { static void Main(string[] args) { long offset = 0x10000000; // 256 megabytes long length = 0x20000000; // 512 megabytes // Create the memory-mapped file. using (var mmf = MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data", FileMode.Open,"ImgA")) { // Create a random access view, from the 256th megabyte (the offset) // to the 768th megabyte (the offset plus length). using (var accessor = mmf.CreateViewAccessor(offset, length)) { int colorSize = Marshal.SizeOf(typeof(MyColor)); MyColor color; // Make changes to the view. for (long i = 0; i < length; i += colorSize) { accessor.Read(i, out color); color.Brighten(10); accessor.Write(i, ref color); } } } } } public struct MyColor { public short Red; public short Green; public short Blue; public short Alpha; // Make the view brighter. public void Brighten(short value) { Red = (short)Math.Min(short.MaxValue, (int)Red + value); Green = (short)Math.Min(short.MaxValue, (int)Green + value); Blue = (short)Math.Min(short.MaxValue, (int)Blue + value); Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value); } }
下面的示例爲另外一個進程打開相同的內存映射文件。
using System; using System.IO.MemoryMappedFiles; using System.Runtime.InteropServices; class Program { static void Main(string[] args) { // Assumes another process has created the memory-mapped file. using (var mmf = MemoryMappedFile.OpenExisting("ImgA")) { using (var accessor = mmf.CreateViewAccessor(4000000, 2000000)) { int colorSize = Marshal.SizeOf(typeof(MyColor)); MyColor color; // Make changes to the view. for (long i = 0; i < 1500000; i += colorSize) { accessor.Read(i, out color); color.Brighten(20); accessor.Write(i, ref color); } } } } } public struct MyColor { public short Red; public short Green; public short Blue; public short Alpha; // Make the view brigher. public void Brighten(short value) { Red = (short)Math.Min(short.MaxValue, (int)Red + value); Green = (short)Math.Min(short.MaxValue, (int)Green + value); Blue = (short)Math.Min(short.MaxValue, (int)Blue + value); Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value); } }
共享內存是內存映射文件的一種特殊狀況,內存映射的是一塊內存,而非磁盤上的文件。共享內存的主語是進程(Process),操做系統默認會給每一 個進程分配一個內存空間,每個進程只容許訪問操做系統分配給它的哪一段內存,而不能訪問其餘進程的。而有時候須要在不一樣進程之間訪問同一段內存,怎麼辦 呢?操做系統給出了建立訪問共享內存的API,須要共享內存的進程能夠經過這一組定義好的API來訪問多個進程之間共有的內存,各個進程訪問這一段內存就 像訪問一個硬盤上的文件同樣。而.Net 4.0中引入了System.IO. MemoryMappedFiles命名空間,這個命名空間的類對windows 共享內存相關API作了封裝,使.Net程序員能夠更方便的使用內存映射文件。
在C#中使用共享內存。如下App1的代碼讓用戶輸入一行文本到共享內存中;App2不停的刷新控制檯,輸出最新的共享內存內容;App3實現的功能和App2相同,但讀取方法不一樣。
App1代碼: using System; using System.Collections.Generic;android從資源文件中讀取文件流顯示 using System.Linq; using System.Text; using System.IO; //引用內存映射文件命名空間 using System.IO.MemoryMappedFiles; namespace App1 { class Program { static void Main(string[] args) { long capacity = 1<<10<<10; //建立或者打開共享內存 using (var mmf = MemoryMappedFile.CreateOrOpen("testMmf", capacity, MemoryMappedFileAccess.ReadWrite)) { //經過MemoryMappedFile的CreateViewAccssor方法得到共享內存的訪問器 var viewAccessor = mmf.CreateViewAccessor(0, capacity); //循環寫入,使在這個進程中能夠向共享內存中寫入不一樣的字符串值 while (true) { Console.WriteLine("請輸入一行要寫入共享內存的文字:"); string input = Console.ReadLine(); //向共享內存開始位置寫入字符串的長度 viewAccessor.Write(0, input.Length); //向共享內存4位置寫入字符 viewAccessor.WriteArray<char>(4, input.ToArray(), 0, input.Length); } } } } }
App2代碼: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; //引用使用內存映射文件須要的命名空間 using System.IO.MemoryMappedFiles; namespace App2 { class Program { static void Main(string[] args) { long capacity = 1<<10<<10; using (var mmf = MemoryMappedFile.OpenExisting("testMmf")) { MemoryMappedViewAccessor viewAccessor = mmf.CreateViewAccessor(0, capacity); //循環刷新共享內存字符串的值 while (true) { //讀取字符長度 int strLength = viewAccessor.ReadInt32(0); char[] charsInMMf = new char[strLength]; //讀取字符 viewAccessor.ReadArray<char>(4, charsInMMf, 0, strLength); Console.Clear(); Console.Write(charsInMMf); Console.Write("\r"); Thread.Sleep(200); } } } } }
App3代碼: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO.MemoryMappedFiles; using System.IO; namespace App3 { class Program { static void Main(string[] args) { long capacity = 1 << 10 << 10; //打開共享內存 using (var mmf = MemoryMappedFile.OpenExisting("testMmf")) { //使用CreateViewStream方法返回stream實例 using (var mmViewStream = mmf.CreateViewStream(0, capacity)) { //這裏要制定Unicode編碼不然會出問題 using (BinaryReader rdr = new BinaryReader(mmViewStream,Encoding.Unicode)) { while (true) { mmViewStream.Seek(0, SeekOrigin.Begin); int length = rdr.ReadInt32(); char[] chars = rdr.ReadChars(length); Console.Write(chars); Console.Write("\r"); System.Threading.Thread.Sleep(200); Console.Clear(); } } } } } } }
在讀數據時用了2種方法。
由於在以前不多會用到進程之間的通訊,因此此方法只是想初步的認識下。此程序寫的過於簡陋,有不少東西都沒有去判斷。好比說是怎麼建立了一個共享內存怎麼取刪除它等等。。。
附NET4.0以前經過WinAPI的調用
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; namespace BlueVision.SaYuan.FileMapping { public class ShareMemory { [DllImport( "user32.dll", CharSet = CharSet.Auto )] public static extern IntPtr SendMessage( IntPtr hWnd, int Msg, int wParam, IntPtr lParam ); [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )] public static extern IntPtr CreateFileMapping( IntPtr hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName ); [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )] public static extern IntPtr OpenFileMapping( int dwDesiredAccess, [MarshalAs( UnmanagedType.Bool )] bool bInheritHandle, string lpName ); [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )] public static extern IntPtr MapViewOfFile( IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap ); [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )] public static extern bool UnmapViewOfFile( IntPtr pvBaseAddress ); [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )] public static extern bool CloseHandle( IntPtr handle ); [DllImport( "kernel32", EntryPoint = "GetLastError" )] public static extern int GetLastError(); [DllImport( "kernel32.dll" )] static extern void GetSystemInfo( out SYSTEM_INFO lpSystemInfo ); [StructLayout( LayoutKind.Sequential )] public struct SYSTEM_INFO { public ushort processorArchitecture; ushort reserved; public uint pageSize; public IntPtr minimumApplicationAddress; public IntPtr maximumApplicationAddress; public IntPtr activeProcessorMask; public uint numberOfProcessors; public uint processorType; public uint allocationGranularity; public ushort processorLevel; public ushort processorRevision; } /// <summary> /// 獲取系統的分配粒度 /// </summary> /// <returns></returns> public static uint GetPartitionsize() { SYSTEM_INFO sysInfo; GetSystemInfo( out sysInfo ); return sysInfo.allocationGranularity; } const int ERROR_ALREADY_EXISTS = 183; const int FILE_MAP_COPY = 0x0001; const int FILE_MAP_WRITE = 0x0002; const int FILE_MAP_READ = 0x0004; const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004; const int PAGE_READONLY = 0x02; const int PAGE_READWRITE = 0x04; const int PAGE_WRITECOPY = 0x08; const int PAGE_EXECUTE = 0x10; const int PAGE_EXECUTE_READ = 0x20; const int PAGE_EXECUTE_READWRITE = 0x40; const int SEC_COMMIT = 0x8000000; const int SEC_IMAGE = 0x1000000; const int SEC_NOCACHE = 0x10000000; const int SEC_RESERVE = 0x4000000; IntPtr m_fHandle; IntPtr m_hSharedMemoryFile = IntPtr.Zero; IntPtr m_pwData = IntPtr.Zero; bool m_bAlreadyExist = false; bool m_bInit = false; uint m_MemSize = 0x1400000;//20M long m_offsetBegin = 0; long m_FileSize = 0; FileReader File = new FileReader(); /// <summary> /// 初始化文件 /// </summary> /// <param name="MemSize">緩衝大小</param> public ShareMemory( string filename, uint memSize ) { // 分頁映射文件時,每頁的起始位置startpos,必須爲64K的整數倍。 // memSize即緩存區的大小必須是系統分配粒度的整倍說,window系統的分配粒度是64KB this.m_MemSize = memSize; Init( filename ); } /// <summary> /// 默認映射20M緩衝 /// </summary> /// <param name="filename"></param> public ShareMemory( string filename ) { this.m_MemSize = 0x1400000; Init( filename ); } ~ShareMemory() { Close(); } /// <summary> /// 初始化共享內存 /// /// 共享內存名稱 /// 共享內存大小 /// </summary> /// <param name="strName"></param> protected void Init( string strName ) { //if (lngSize <= 0 || lngSize > 0x00800000) lngSize = 0x00800000; if ( !System.IO.File.Exists( strName ) ) throw new Exception( "未找到文件" ); System.IO.FileInfo f = new System.IO.FileInfo( strName ); m_FileSize = f.Length; m_fHandle = File.Open( strName ); if ( strName.Length > 0 ) { //建立文件映射 m_hSharedMemoryFile = CreateFileMapping( m_fHandle, IntPtr.Zero, ( uint )PAGE_READONLY, 0, ( uint )m_FileSize, "mdata" ); if ( m_hSharedMemoryFile == IntPtr.Zero ) { m_bAlreadyExist = false; m_bInit = false; throw new Exception( "CreateFileMapping失敗LastError=" + GetLastError().ToString() ); } else m_bInit = true; ////映射第一塊文件 //m_pwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_READ, 0, 0, (uint)m_MemSize); //if (m_pwData == IntPtr.Zero) //{ // m_bInit = false; // throw new Exception("m_hSharedMemoryFile失敗LastError=" + GetLastError().ToString()); //} } } /// <summary> /// 獲取高32位 /// </summary> /// <param name="intValue"></param> /// <returns></returns> private static uint GetHighWord( UInt64 intValue ) { return Convert.ToUInt32( intValue >> 32 ); } /// <summary> /// 獲取低32位 /// </summary> /// <param name="intValue"></param> /// <returns></returns> private static uint GetLowWord( UInt64 intValue ) { return Convert.ToUInt32( intValue & 0x00000000FFFFFFFF ); } /// <summary> /// 獲取下一個文件塊 塊大小爲20M /// </summary> /// <returns>false 表示已是最後一塊文件</returns> public uint GetNextblock() { if ( !this.m_bInit ) throw new Exception( "文件未初始化。" ); //if ( m_offsetBegin + m_MemSize >= m_FileSize ) return false; uint m_Size = GetMemberSize(); if ( m_Size == 0 ) return m_Size; // 更改緩衝區大小 m_MemSize = m_Size; //卸載前一個文件 //bool l_result = UnmapViewOfFile( m_pwData ); //m_pwData = IntPtr.Zero; m_pwData = MapViewOfFile( m_hSharedMemoryFile, FILE_MAP_READ, GetHighWord( ( UInt64 )m_offsetBegin ), GetLowWord( ( UInt64 )m_offsetBegin ), m_Size ); if ( m_pwData == IntPtr.Zero ) { m_bInit = false; throw new Exception( "映射文件塊失敗" + GetLastError().ToString() ); } m_offsetBegin = m_offsetBegin + m_Size; return m_Size; //建立成功 } /// <summary> /// 返回映射區大小 /// </summary> /// <returns></returns> private uint GetMemberSize() { if ( m_offsetBegin >= m_FileSize ) { return 0; } else if ( m_offsetBegin + m_MemSize >= m_FileSize ) { long temp = m_FileSize - m_offsetBegin; return ( uint )temp; } else return m_MemSize; } /// <summary> /// 關閉內存映射 /// </summary> public void Close() { if ( m_bInit ) { UnmapViewOfFile( m_pwData ); CloseHandle( m_hSharedMemoryFile ); File.Close(); } } /// <summary> /// 從當前塊中獲取數據 /// </summary> /// <param name="bytData">數據</param> /// <param name="lngAddr">起始數據</param> /// <param name="lngSize">數據長度,最大值=緩衝長度</param> /// <param name="Unmap">讀取完成是否卸載緩衝區</param> /// <returns></returns> public void Read( ref byte[] bytData, int lngAddr, int lngSize, bool Unmap ) { if ( lngAddr + lngSize > m_MemSize ) throw new Exception( "Read操做超出數據區" ); if ( m_bInit ) { // string bb = Marshal.PtrToStringAuto(m_pwData);// Marshal.Copy( m_pwData, bytData, lngAddr, lngSize ); } else { throw new Exception( "文件未初始化" ); } if ( Unmap ) { bool l_result = UnmapViewOfFile( m_pwData ); if ( l_result ) m_pwData = IntPtr.Zero; } } /// <summary> /// 從當前塊中獲取數據 /// </summary> /// <param name="bytData">數據</param> /// <param name="lngAddr">起始數據</param> /// <param name="lngSize">數據長度,最大值=緩衝長度</param> /// <exception cref="Exception: Read操做超出數據區"></exception> /// <exception cref="Exception: 文件未初始化"></exception> /// <returns></returns> public void Read( ref byte[] bytData, int lngAddr, int lngSize ) { if ( lngAddr + lngSize > m_MemSize ) throw new Exception( "Read操做超出數據區" ); if ( m_bInit ) { Marshal.Copy( m_pwData, bytData, lngAddr, lngSize ); } else { throw new Exception( "文件未初始化" ); } } /// <summary> /// 從當前塊中獲取數據 /// </summary> /// <param name="lngAddr">緩存區偏移量</param> /// <param name="byteData">數據數組</param> /// <param name="StartIndex">數據數組開始複製的下標</param> /// <param name="lngSize">數據長度,最大值=緩衝長度</param> /// <exception cref="Exception: 起始數據超過緩衝區長度"></exception> /// <exception cref="Exception: 文件未初始化"></exception> /// <returns>返回實際讀取值</returns> public uint ReadBytes( int lngAddr, ref byte[] byteData, int StartIndex, uint intSize ) { if ( lngAddr >= m_MemSize ) throw new Exception( "起始數據超過緩衝區長度" ); if ( lngAddr + intSize > m_MemSize ) intSize = m_MemSize - ( uint )lngAddr; if ( m_bInit ) { IntPtr s = new IntPtr( ( long )m_pwData + lngAddr ); // 地址偏移 Marshal.Copy( s, byteData, StartIndex, ( int )intSize ); } else { throw new Exception( "文件未初始化" ); } return intSize; } /// <summary> /// 寫數據 /// </summary> /// <param name="bytData">數據</param> /// <param name="lngAddr">起始地址</param> /// <param name="lngSize">個數</param> /// <returns></returns> private int Write( byte[] bytData, int lngAddr, int lngSize ) { if ( lngAddr + lngSize > m_MemSize ) return 2; //超出數據區 if ( m_bInit ) { Marshal.Copy( bytData, lngAddr, m_pwData, lngSize ); } else { return 1; //共享內存未初始化 } return 0; //寫成功 } } internal class FileReader { const uint GENERIC_READ = 0x80000000; const uint OPEN_EXISTING = 3; System.IntPtr handle; [DllImport( "kernel32", SetLastError = true )] public static extern System.IntPtr CreateFile( string FileName, // file name uint DesiredAccess, // access mode uint ShareMode, // share mode uint SecurityAttributes, // Security Attributes uint CreationDisposition, // how to create uint FlagsAndAttributes, // file attributes int hTemplateFile // handle to template file ); [System.Runtime.InteropServices.DllImport( "kernel32", SetLastError = true )] static extern bool CloseHandle ( System.IntPtr hObject // handle to object ); public IntPtr Open( string FileName ) { // open the existing file for reading handle = CreateFile ( FileName, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0 ); if ( handle != System.IntPtr.Zero ) { return handle; } else { throw new Exception( "打開文件失敗" ); } } public bool Close() { return CloseHandle( handle ); } } }
注:以上部分轉自其餘人博客的代碼。