我需要读取一个空间分隔的大文本文件,并计算文件中每个代码的实例数。从本质上讲,这些是运行一些实验数十万次的结果。系统会发出一个看起来像这样的文本文件:
A7PS A8PN A6PP23 ...
这些条目实际上有成千上万,我需要计算每个代码的出现次数。
我想我可以打开StreamReader
并逐行浏览,分割空格字符。查看是否已遇到代码并将该代码的计数加1。但是,考虑到数据的大小,这可能很幼稚。
有人知道处理这种处理的有效算法吗?
更新:
好的,所以共识似乎是我的方法是沿着正确的方向
我有兴趣听到的内容是 - 更高效 - StreamReader。 TextReader,BinaryReader
存储结果词典的最佳结构是什么? HashTable,SortedList,HybridDictionary
如果文件中没有换行符(我还没有给出样本),只是将整个事物分成一个空间效率低下吗?
基本上,我希望尽可能提高性能
再次感谢
答案 0 :(得分:5)
你的方法看起来很好。
答案 1 :(得分:4)
我会说,一般来说,你的方法是对的,但仍有并行性的余地。我建议您启动多个线程或任务(在.NET 4中)每个解析部分/文件块。 而不是逐行读取,读取大块字节 - 将从磁盘IO角度提供更好的性能。
修改:以下是解决方案的大纲。
当然,我假设采用这种方法的文件非常庞大。我可能会在缓冲区中使用旧式字符查找来查找字边界标记查找代码是不安全的,以避免绑定检查。
答案 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
由于似乎不需要排序顺序,HybridDictionary或Dictionary在大多数情况下会更好。 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秒。
对我来说似乎非常有效。