• 统计一个大文本行数的几种方法以及效率统计(一)


    最近在和一个朋友的交流中,遇到这么一个问题,如何能较快对一个较大的文本文件(1G或更大)的文本行数进行统计。如果不考虑效率,要统计一个文本的行数其实一点也不难,但是如果需要在较快的时间内做完,恐怕就得考虑实现方法了。

    为此,自己尝试了几种方法,在这里把这几种方法拿出来和大家讨论一下。

    首先是生成测试数据的代码:

            const int COL_NUM = 30;
            const int LINE_NUM = 10000000;
            const string FILE_NAME = @"d:\test.csv";
    
            /// <summary>
            /// 创建一个一千万行的文本文件,大小约为900M。
            /// </summary>
            static void CreateTestCSVFile()
            {
                string rowValue = string.Join(string.Empty, 
                    Enumerable.Range(1, COL_NUM).Select(i => i.ToString("00") + ","));
                
                using (StreamWriter sw = new StreamWriter(FILE_NAME, false, Encoding.ASCII))
                {
                    for (int i = 0; i < LINE_NUM; i++)
                    {
                        sw.WriteLine(rowValue);
                    }
    
                }
            }
    
    

    .NET4.0 + StreamReader + ReadLine()

    原理很简单,使用StreamReader的ReadLine方法,每执行一次,行数加一。代码如下:

            /// <summary>
            /// StreamReader + ReadLine()
            /// </summary>
            static void CalculateLine_ReadLine()
            {
                long lineCount = 0;
                using (StreamReader sr = new StreamReader(FILE_NAME, false))
                {
                    while (!sr.EndOfStream)
                    {
                        sr.ReadLine();
                        lineCount++;
                    }
                }
                Console.WriteLine("line count: {0}", lineCount);
            }

    测试结果如下:

    StreamReader ReadLine()

    对于以上这种方法,平均每次执行时间为55s左右,执行效率明显不尽如人意。

    如果我们同样采用流,不过使用分块的方式,将文件内容一块一块读进内存,在解析每块内容的行数,最后相加。这样做的效率如何呢?

    .NET4.0 + StreamReader + ReadBlock()

            /// <summary>
            /// StreamReader + ReadBlock()
            /// </summary>
            static void CalculateLine_ReadBlock(int oneBlockSize)
            {
                
                long lineCount = 0;
                char[] oneBlock = new char[oneBlockSize];
                using (StreamReader sr = new StreamReader(FILE_NAME, false))
                {
                    long streamLen = sr.BaseStream.Length;
                    while (!sr.EndOfStream)
                    {
                        long leftLength = streamLen - sr.BaseStream.Position - 1;
                        if (leftLength >= oneBlockSize)
                        {
                            sr.ReadBlock(oneBlock, 0, oneBlock.Length);
                            lineCount += oneBlock.Count(c => c == '\r');
                        }
                        else
                        {
                            lineCount += sr.ReadToEnd().Count(c => c == '\r');
                        }
                    }
                }
    
                Console.WriteLine("line count: {0}", lineCount);
            }
    
            static void Main(string[] args)
            {
    
                int[] oneBlockArray = new[] { 1, 10, 20, 50, };
                foreach (int oneBlockSize in oneBlockArray)
                {
                    
                    CodeTimerF2.CodeTimer.Time(string.Format("StreamReader + ReadBolck() [Block Size: {0}MB]", oneBlockSize),
                        1, () => CalculateLine_ReadBlock(oneBlockSize * 1024 * 1024));
                }
                
                Console.ReadKey();
            }

    运行结果如图:

    StreamReader ReadBlock()

  • 相关阅读:
    XMLHttpRequest 跨域问题
    jQuery+AJAX实现纯js分页功能
    PHP验证码
    PHP基础
    UIView易忽略点
    UITableView  优化
    SpringBoard界面层级结构分析
    给App在“设置”中添加选项(类似招行App)
    通过USB线SSH登陆到越狱手机上(命令行模式的),不通过wifi
    在IOS项目中使用Lua
  • 原文地址:https://www.cnblogs.com/quark/p/1941109.html
Copyright © 2020-2023  润新知