在C#中高效解析大型文本文件

时间:2010-08-27 11:54:38

标签: c# algorithm parsing text-processing

我需要读取一个空间分隔的大文本文件,并计算文件中每个代码的实例数。从本质上讲,这些是运行一些实验数十万次的结果。系统会发出一个看起来像这样的文本文件:

A7PS A8PN A6PP23 ...

这些条目实际上有成千上万,我需要计算每个代码的出现次数。

我想我可以打开StreamReader并逐行浏览,分割空格字符。查看是否已遇到代码并将该代码的计数加1。但是,考虑到数据的大小,这可能很幼稚。

有人知道处理这种处理的有效算法吗?

更新:

好的,所以共识似乎是我的方法是沿着正确的方向

我有兴趣听到的内容是 - 更高效 - StreamReader。 TextReader,BinaryReader

存储结果词典的最佳结构是什么? HashTable,SortedList,HybridDictionary

如果文件中没有换行符(我还没有给出样本),只是将整个事物分成一个空间效率低下吗?

基本上,我希望尽可能提高性能

再次感谢

8 个答案:

答案 0 :(得分:5)

你的方法看起来很好。

  1. 每行读入
  2. 按空格分割每一行
  3. 将记录添加到字典中  如果它还不存在 如果确实存在,请执行值++

答案 1 :(得分:4)

我会说,一般来说,你的方法是对的,但仍有并行性的余地。我建议您启动多个线程或任务(在.NET 4中)每个解析部分/文件块。 而不是逐行读取,读取大块字节 - 将从磁盘IO角度提供更好的性能。

修改:以下是解决方案的大纲。

  1. 假设我们将处理M块 当时有N个字符(因为 我们想限制内存量 需要和使用的线程数。)。
  2. 分配N * M个字符缓冲区。我们将循环使用此缓冲区。
  3. 将使用生产者 - 消费者模式。 制片人将填补缓冲区。它 将尝试在附近找到单词边界 块边界(即每隔一个N. 字符)。所以我们将有M块 大约N个字符的开头 和缓冲区内的结束索引
  4. 现在启动M个工作线程来处理每个块。每个工作人员都会使用自己的字典来计算单词 - 这将消除对线程同步的需求。
  5. 将在迭代结束时聚合结果。需要重复该过程,直到读取整个文件。
  6. 当然,我假设采用这种方法的文件非常庞大。我可能会在缓冲区中使用旧式字符查找来查找字边界标记查找代码是不安全的,以避免绑定检查。

答案 2 :(得分:1)

我同意PoweRoy的评论:为什么不试试呢?也许在实践中没有问题。

如果您确实需要其他内容,可以尝试编写一些采用Stream并返回IEnumerable<string>的代码。它会一次从输入中读取一个字符 - 如果你需要缓冲以提高效率,你总是可以将FileStream实际包含在BufferStream中,并检查它是否是一个空格(或者可能是EOL?)。如果不是,它会将字符添加到字符串缓冲区(可能是StringBuilder?),但如果是,它将yield return当前字符串缓冲区并清除它。

之后,您只需foreach就文件内容调用此代码的结果,您将逐个从文件中获取代码。

然后,您可以使用某种数据结构(如Dictionary<string,int>)来计算每个代码的出现次数,将代码保持为键,将计数保留为值。但是,如果您逐行读取文件并使用string.Split将它们拆分为空格,则此步骤将相同。

答案 3 :(得分:1)

如果你想尝试不同的东西,你可以尝试使用BinaryReader,并逐字节地读取流,并在每次遇到空格时将计数器增加一。

答案 4 :(得分:1)

数十万条记录并非如此。我会使用Dictionary<string,int>。存储密钥和计数。

但是如果遇到内存问题,为什么不使用数据库,甚至是SQL Compact或SQLite等数据库。创建一个包含密钥和计数的记录的表。

将数据保存在内存中对于少量数据来说是最快的,但是当你达到计算机内存限制时,数据库会更快。

答案 5 :(得分:0)

在一个非常基础的层面上,我从一个Dictionary<string, int>开始,string.split空格上的文档,并通过简单的解析数据来保持计数。

string.split是一个相对健壮的方法,如果我错了,有人肯定会纠正我,它是为了使用正则表达式而构建的,并且比你在这个场景中所需要的要复杂得多。

编写自己的拆分方法可能比框架中的解决方案更可行。我建议先如上所述使用现成的版本,然后在确定性能问题时重写自己的版本。

伊恩

答案 6 :(得分:0)

如果没有其他限制,您必须按照描述阅读完整文件。

要保存代码和计数,您应该使用允许搜索和插入O(log n)时间的数据结构。 SortedDictionary将在C#中执行此操作。

编辑:

  

存储结果词典的最佳结构是什么? HashTable,SortedList,HybridDictionary

由于似乎不需要排序顺序,HybridDictionaryDictionary在大多数情况下会更好。 SortedList可能是最慢的解决方案,因为插入需要O(n)。如果性能如此重要,您应该对不同的实现进行一些测试。

答案 7 :(得分:0)

    static string LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    static string NUMBERS = "1234567890";
    static Random rdGen = new Random();
    static Dictionary<string, int> myDic = new Dictionary<string, int>();
    static void WriteTest(int max)
    {
        myDic = new Dictionary<string, int>();
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < max; i++)
        {
            string code = LETTERS[rdGen.Next(0, 26)].ToString() + NUMBERS[rdGen.Next(0, 10)].ToString() + LETTERS[rdGen.Next(0, 26)].ToString() + LETTERS[rdGen.Next(0, 26)].ToString();
            if (myDic.ContainsKey(code)) myDic[code]++;
            else
            {
                myDic[code] = 1;
            }
        }
        sw.Stop();
        Console.WriteLine(max.ToString() + " itérations : " + sw.ElapsedMilliseconds.ToString());

    }

WriteTest(10000000); //需要7.5秒。

对我来说似乎非常有效。