在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
由项目需要,需要扫描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/
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论