[英]Efficiently parsing a large text file in C#
我需要读取一个空间分隔的大文本文件,并计算文件中每个代码的实例数。 从本质上讲,这些是运行一些实验数十万次的结果。 系统会吐出一个看起来像这样的文本文件:
A7PS A8PN A6PP23 ...
实际上有数十万个这样的条目,我需要计算每个代码的出现次数。
我想我可以打开一个StreamReader
并逐行浏览,拆分空格字符。 查看是否已遇到代码并将该代码的计数加1。 但是,考虑到数据的大小,这可能很幼稚。
有人知道处理这种处理的有效算法吗?
更新:
好的,所以共识似乎是我的方法是沿着正确的方向
我有兴趣听到的是 - 更有效的 - StreamReader。 TextReader,BinaryReader
存储结果字典的最佳结构是什么? HashTable,SortedList,HybridDictionary
如果文件中没有换行符(我还没有给出样本)只会将整个事物拆分到空间上效率低下吗?
从本质上讲,我期待尽可能提高性能
再次感谢
你的方法看起来很好。
我会说,一般来说,你的方法是正确的,但仍有并行性的余地。 我建议您启动多个线程或任务(在.NET 4中)每个解析部分/文件块。 而不是逐行读取,读入大块字节 - 将从磁盘IO角度提供更好的性能。
编辑 :这是解决方案的大纲。
当然,我假设采用这种方法的文件非常庞大。 我可能会在缓冲区中使用旧式字符查找来查找字边界标记查找代码是不安全的,以避免绑定检查。
我同意PoweRoy的评论:为什么不尝试一下呢? 也许在实践中没有问题。
如果你确实需要别的东西,你可以尝试编写一些带有Stream
代码并返回一个IEnumerable<string>
。 它会一次从输入中读取一个字符 - 如果你需要缓冲以提高效率,你总是可以将实际上在BufferStream
提供此代码的FileStream
包装起来 - 并检查它是否是空格(或者可能是EOL?)。 如果不是,它会将字符添加到字符串缓冲区(可能是StringBuilder
?),但如果是,它将yield return
当前字符串缓冲区并清除它。
之后,您可以foreach
知道在文件内容上调用此代码的结果,您将逐个从文件中获取代码。
然后,您可以使用某种数据结构(如Dictionary<string,int>
来计算每个代码的出现次数,将代码保持为键,将计数保留为值。 但是,如果逐行读取文件并使用string.Split
将它们拆分为空格,则此步骤将相同。
如果你想尝试不同的东西,你可以尝试使用BinaryReader
,并逐字节读取流,并在每次遇到空格时将计数器增加一。
十万条记录并非如此。 我会使用Dictionary<string,int>
。 存储密钥和计数。
但是如果遇到内存问题,为什么不使用数据库,甚至是SQL Compact或SQLite等数据库。 创建一个包含密钥和计数的记录的表。
将数据保存在内存中对于少量数据来说是最快的,但是当您达到计算机内存限制时,数据库将更快。
在一个非常基础的层面上,我将开始使用Dictionary<string, int>
,string.split空间上的文档,并通过简单解析该数据来保持计数。
string.split是一个相对健壮的方法,如果我错了,有人肯定会纠正我,它是为了使用正则表达式构建的,并且比这个场景所需要的要复杂得多。
编写自己的split方法可能比框架中的方法更可行。 我建议先如上所述使用现成版本,然后在确定性能有问题时重写自己的版本。
伊恩
如果没有其他限制,您必须按照描述阅读完整文件。
要保存代码和计数,您应该使用允许搜索和插入O(log n)时间的数据结构。 SortedDictionary将在C#中执行此操作。
编辑:
存储结果字典的最佳结构是什么? HashTable,SortedList,HybridDictionary
因为似乎不需要排序顺序,所以HybridDictionary或Dictionary在大多数情况下都会表现得更好。 SortedList可能是最慢的解决方案,因为插入需要O(n)。 如果性能如此重要,您应该对不同的实现进行一些测试。
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秒。
对我来说似乎非常有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.