zoukankan      html  css  js  c++  java
  • 快速扫描文本文件,统计行数,并返回每一行的索引位置(Delphi、C#)

    由项目需要,需要扫描1200万行的文本文件。经网友的指点与测试,发现C#与Delphi之间的差距并不大。不多说,列代码测试:

    下面是Delphi的代码:

    复制代码
    //遍历文件查找回车出现的次数
    function ScanEnterFile(const FileName:string):TInt64Array;
    var
      MyFile:TMemoryStream;//文件内存
      rArray:TInt64Array;       //行索引结果集
      size,curIndex:int64;//文件大小,当前流位置
      enterCount:int64;//回车数量
      DoLoop:Boolean;//是否继续循环
      pc: PChar;
      arrayCount:int64;//当前索引数组大小
      addStep:integer;//检测到回车字符串时需要添加的步进
    begin
      if fileName = '' then
        Exit;
      if not FileExists(fileName) then
        Exit;
      MyFile:=TMemoryStream.Create;//创建流
      MyFile.LoadFromFile(fileName);//把流入口映射到MyFile对象
      size:=MyFile.Size;
      pc:=MyFile.Memory; //把字符指针指向内存流
      curIndex:=RowLeast;
      DoLoop:=true;
      enterCount:=0;
      setlength(rArray,perArray);
      arrayCount:=perArray;
      enterCount:=0;
      rArray[enterCount]:=0;
      while DoLoop do
      begin
        addStep:=0;
        if (ord(pc[curIndex])=13) then
          addStep:=2;
        if (ord(pc[curIndex])=10) then
          addStep:=1;
        //处理有回车的
        if (addStep<>0) then
        begin
          Application.ProcessMessages;
          //增加一行记录
          inc(enterCount);
          //判断是否需要增大数组
          if (enterCount mod perArray=0) then
          begin
            arrayCount:=arrayCount+perArray;
            setlength(rArray,arrayCount);
          end;
          rArray[enterCount]:=curIndex+addStep;
          curIndex:=curIndex+addStep+RowLeast;
        end
        else
          curIndex:=curIndex+2;
        if curIndex> size then
          DoLoop:=false
        else
          DoLoop:=true;
      end;
      result:=rArray;
      freeandnil(MyFile);
    end;
    复制代码

    执行代码:

    复制代码
    procedure TMainForm.btn2Click(Sender: TObject);
    var
      datasIndex:TInt64Array;//数据文件索引
    begin

      t1:=GetTickCount;
      datasIndex:=ScanEnterFile('R:201201_dataFile.txt');
      Caption:=Caption+'::'+inttostr(GetTickCount-t1); 
    end;
    复制代码

    执行结果是:16782 ms

    下面是C#的代码:

    复制代码
            /// <summary>
            /// 扫描文本文件,进行行数的统计,并返回每一行的开始指针数组(1.2KW数据速度比使用数组的快10秒)
            /// </summary>
            /// <param name="fileName">文件名</param>
            /// <param name="rowCount">行数</param>
            /// <param name="rowLeast">一行最小长度</param>
            /// <param name="incCount">递增索引数组数量</param>
            /// <param name="initCount">首次初始化行索引数量</param>
            /// <returns>索引列表</returns>
            public static IList<long> ScanEnterFile(string fileName, out int rowCount, int rowLeast,ThreadProgress progress)
            {
                rowCount = 0;
                if (string.IsNullOrEmpty(fileName))
                    return null;
                if (!System.IO.File.Exists(fileName))
                    return null;
                FileStream myFile = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 8);//把文件读入流
                IList<long> rList=new List<long>();
                int enterCount = 0;//回车数量
                int checkValue;
                int addStep;
                myFile.Position = rowLeast;
                checkValue = myFile.ReadByte();
                while (checkValue != -1)
                {
                    //Application.DoEvents();
                    addStep = -1;
                    //由于文件ReadByte之后,其当前位置已经往后推移了移位。
                    //因此,如果是回车的第一个字符,则要推移一位。
                    //而如果是回车的第二个字符,则不用推移一位
                    if (checkValue == 13)
                        addStep = 1;
                    else if (checkValue == 10)
                        addStep = 0;
                    if (addStep >= 0)
                    {
                        enterCount++;
                        rList.Add(myFile.Position + addStep);
                        myFile.Seek(rowLeast + addStep, SeekOrigin.Current);
                        progress(enterCount);
                    }
                    else myFile.Seek(2, SeekOrigin.Current);
                    checkValue = myFile.ReadByte();
                }
                rowCount = enterCount + 1;
                return rList;
            }
    复制代码

    执行的代码:

                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                int rowCount;
                FileHelper.ScanEnterFile(@"R:201201_dataFile.txt", out rowCount, 35, outputProgress);
                useTime = stopwatch.ElapsedMilliseconds;

    执行结果是:

    124925  ms

    (经过众多网友的批评与指点,该方法并没有把文件读取内存中,而是逐个字节地读取,速度比Delphi字节读进内存的方法要慢很多。这种方法只适合于老机器,内存不够的情况下,当今内存已经很便宜了,所以,该方法目前已经过时了,下面经过网友的指点,使用了readline的方法,速度大概是6秒左右。)

    复制代码
            public static IList<long> ScanEnterFile(string fileName, ThreadProgress progress)
            {
                if (string.IsNullOrEmpty(fileName))
                    return null;
                if (!System.IO.File.Exists(fileName))
                    return null;
                IList<long> rList = new List<long>();
                rList.Add(0);
                StreamReader sr = File.OpenText(fileName);
                string rStr = sr.ReadLine();
                while (null != rStr)
                {
                    rList.Add(rList[rList.Count-1] + rStr.Length + 2);
                    rStr = sr.ReadLine();
                    progress(rList.Count);
                }
                sr.Close();
                return rList;
            }
    复制代码

    经过测试,该方法如果存在中文字符编码的时候,其位置是错误的。日后找到解决方法后,再上来更新。

    经过测试,C#的使用IList<T>比数组的要快。

    总结:任何事物都有其存在的价值,至于看官门选什么,就根据自己的需要,来选择,这里,本人不会有任何偏向于哪一方。反正,能成事,什么都不重要了。

    原创作品出自努力偷懒,转载请说明文章出处:http://blog.csdn.net/kfarvid或 http://www.cnblogs.com/kfarvid/ 

    http://www.cnblogs.com/kfarvid/archive/2012/01/12/2320692.html

  • 相关阅读:
    javascript深入理解js闭包
    hibernate 之 sql查询
    MongoDB 2.4企业版分析
    MongoDB 连接池
    GridFS实现原理
    MongoVUE破解
    mongodb 官方 手册
    mongodb的一些性能管理工具
    Python: names, values, assignment and mutability
    使用 mock 测试
  • 原文地址:https://www.cnblogs.com/findumars/p/5951313.html
Copyright © 2011-2022 走看看