【转】C#大文件读取和查询--内存映射

笔者最近需要快速查询日志文件,文件大小在4G以上。

需求如下:

1.读取4G左右大小的文件中的指定行,程序运行占用内存不超过500M。

2.希望查询1G以内容,能控制在20s左右.

刚开始觉得这个应该不难.研究一天之后,发现这个需要使用内存映射技术。

查阅了相关资料之后

https://msdn.microsoft.com/zh-cn/library/dd997372(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1

发现还是有一定的复杂性.特别是需要对字符处理。

笔者自己写了一个Demo,希望实现

很遗憾,测试结果,查询1G左右的内容,花费时间在100s左右.

程序如下:

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Text;

namespace ConsoleDemo
{
    class Program
    {
        private const string TXT_FILE_PATH = @"E:\开源学习\超大文本文件读取\File\a.txt";
        private const string SPLIT_VARCHAR = "囧";
        private const char SPLIT_CHAR = ‘囧‘;
        private static long FILE_SIZE = 0;
        static void Main(string[] args)
        {
            //long ttargetRowNum = 39999999;
            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);
                                        }
                                    }
                                    //sbDefineRowLine.Append(ss);
                                    //line = sbDefineRowLine.ToString();
                                    //if (ss.Contains(Environment.NewLine))
                                    //{
                                    //    ++curRowNum;
                                    //    //curRowNum++;
                                    //    //curRowNum += GetNewLineNumsOfStr(ss);
                                    //    //sbDefineRowLine.Append(ss);
                                    //}
                                    //if (curRowNum == targetRowNum)
                                    //{
                                    //    string s = "";
                                    //}

                                    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;
        }
    }
}

  

欢迎大家提供更好的解决思路.

参考资料:

https://msdn.microsoft.com/zh-cn/library/dd997372(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1

http://blog.csdn.net/onejune2013/article/details/7577152

转自:http://www.cnblogs.com/lucky_hu/p/5345423.html

时间: 2024-10-26 13:04:52

【转】C#大文件读取和查询--内存映射的相关文章

C#大文件读取和查询--内存映射

笔者最近需要快速查询日志文件,文件大小在4G以上. 需求如下: 1.读取4G左右大小的文件中的指定行,程序运行占用内存不超过500M. 2.希望查询1G以内容,能控制在20s左右. 刚开始觉得这个应该不难.研究一天之后,发现这个需要使用内存映射技术. 查阅了相关资料之后 https://msdn.microsoft.com/zh-cn/library/dd997372(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1 发现还是

生成器来解决大文件读取,大数据下载

目录 场景 下载文件 读取大文件 场景 电商平台想要导出一年的报表数据,数据可能有百万,平常的做法是查出所有数据放到数组或对象中,再进行excel导出,一般情况下,数据不是很多这种是没什么问题,但百万级别的数据一下读到内存中,服务器会一下崩溃,内存溢出.通常情况下也不会做这种需求,产品提出来你可以骂两句怼回去,但老板说我就需要这个功能,你苦口婆心说几句,但是还是要做啊.生成器可以帮我做到这些,理解概念可以看看这里. 下载文件 //实现下载大文件,解决内存溢出 public function ac

TCP协议传输大文件读取时候的问题

TCP协议传输大文件读取时候的问题 大文件传不完的bug 我们在定义的时候定义服务端每次文件读取大小为10240, 客户端每次接受大小为10240 我们想当然的认为客户端每次读取大小就是10240而把客户端的读下来的文件想当然大小每一次都加上10240 而实际上服务端发送文件send每次发送不一定是一次性把10240的文件传送完,可能分了好几次进行发送至缓冲区这我们实际文件大小就不一定是10240 解决办法: 1.对于每次服务端所发送的文件内容及大小都发送给客户端,让客户端一一对应读取 2.实时

java 导出 excel 最佳实践,java 大文件 excel 避免OOM(内存溢出) exce

产品需求 产品经理需要导出一个页面的所有的信息到 EXCEL 文件. 需求分析 对于 excel 导出,是一个很常见的需求. 最常见的解决方案就是使用 poi 直接同步导出一个 excel 文件. 客户体验 & 服务性能 客户体验 如果导出的文件比较大,比如几十万条数据,同步导出页面就会卡主,用户无法进行其他操作. 服务性能 导出的时候,任务比较耗时就会阻塞主线程. 如果导出的服务是暴露给外部(前后端分离),这种大量的数据传输十分消耗性能. 解决方案 使用异常处理导出请求,后台 MQ 通知自己进

**大文件读取

python 读取文件方式: 1. read 一个字符一个字符的读出全部数据,放到内存 2. readline 一个字符一个字符的读出第一行,放到内存 3. readlines 一行一行的读出全部数据,放到内存 4. 直接通过文件对象读取 区别上述3中方式,逐行放入内存,不是全部放入内存. f = open('sp1.py', encoding='utf-8') for i in f: print(i) 读取大文件时,适合使用这种方式.

java 导出 excel 最佳实践,大文件 excel 避免OOM(内存溢出) 框架-02-API

项目简介 IExcel 用于优雅地读取和写入 excel. 避免大 excel 出现 oom,简约而不简单.. 特性 OO 的方式操作 excel,编程更加方便优雅. sax 模式读取,SXSS 模式写入.避免 excel 大文件 OOM. 基于注解,编程更加灵活. 写入可以基于对象列表,也可以基于 Map,实际使用更加方便. 设计简单,注释完整.方便大家学习改造. 变更日志 变更日志 v0.0.4 主要变化 引入 ExcelBs 引导类,优化使用体验. 创作缘由 实际工作和学习中,apache

php使用file函数、fseek函数读取大文件效率分析

php读取大文件可以使用file函数和fseek函数,但是二者之间效率可能存在差异,本文章向大家介绍php file函数与fseek函数实现大文件读取效率对比分析,需要的朋友可以参考一下. 1. 直接采用file函数来操作 由于 file函数是一次性将所有内容读入内存,而PHP为了防止一些写的比较糟糕的程序占用太多的内存而导致系统内存不足,使服务器出现宕机,所以默认情况下限制只能最大使用内存16M,这是通过php.ini里的 memory_limit = 16M 来进行设置,这个值如果设置-1,

读取大文件时的优化经验

最近在编写一个关于图形学的东西时,由于需要读取模型,写了一个obj文件和mtl文件解析器.实际调试时,由于该文件较长,比如obj文件达到了20万行的量级,在解析时凸显出了各种性能问题,解决这些性能问题的同时,也总结出了一些经验,记录如下: 1 必须使用缓冲区.虽然操作系统实现读取文件应该是有缓冲区概念的,但是结果显示如果不使用缓冲区,而用fgetc挨个字符进行读取,速度会比使用缓冲区慢上1个数量级.因此,引出第一条经验:一切大文件读取必须使用缓冲区,减少fread或fgetc的次数. 2 关于m

VC++ 中使用内存映射文件处理大文件

摘要: 本文给出了一种方便实用的解决大文件的读取.存储等处理的方法,并结合相关程序代码对具体的实现过程进行了介绍. 引言 文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类,常用的有Win32 API的CreateFile().WriteFile().ReadFile()和MFC提供的CFile类等.一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB.几百GB.乃至几TB的海量存储,再以通常的文件处理方法进行处