最近在和一个朋友的交流中,遇到这么一个问题,如何能较快对一个较大的文本文件(1G或更大)的文本行数进行统计。如果不考虑效率,要统计一个文本的行数其实一点也不难,但是如果需要在较快的时间内做完,恐怕就得考虑实现方法了。
为此,自己尝试了几种方法,在这里把这几种方法拿出来和大家讨论一下。
首先是生成测试数据的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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方法,每执行一次,行数加一。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /// <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); } |
测试结果如下:
对于以上这种方法,平均每次执行时间为55s左右,执行效率明显不尽如人意。
如果我们同样采用流,不过使用分块的方式,将文件内容一块一块读进内存,在解析每块内容的行数,最后相加。这样做的效率如何呢?
.NET4.0 + StreamReader + ReadBlock()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | /// <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(); } |
运行结果如图:
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法