quark

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  49 随笔 :: 10 文章 :: 40 评论 :: 19万 阅读
< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

最近在和一个朋友的交流中,遇到这么一个问题,如何能较快对一个较大的文本文件(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);
}

测试结果如下:

StreamReader ReadLine()

对于以上这种方法,平均每次执行时间为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();
}

运行结果如图:

StreamReader ReadBlock()

posted on   QuarkZ  阅读(2063)  评论(0编辑  收藏  举报
编辑推荐:
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
阅读排行:
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法
点击右上角即可分享
微信分享提示