是否有更有效的方法来迭代文件集合并构建文件内容的字典
本文关键字:文件 合并 集合 构建 字典 迭代 有效 方法 是否 | 更新日期: 2023-09-27 18:11:07
我有以下代码。是否有更有效的方法来完成同样的任务?
- 给定一个文件夹,遍历文件夹中的文件。
- 在每个文件中,跳过头四行,
- 基于空格分隔行后,如果生成的数组包含少于7个元素,则跳过它,
- 检查指定的元素是否已经在字典中。如果是,则增加计数。如果没有,请添加。
这不是个复杂的过程。有更好的方法吗?LINQ吗?
string sourceDirectory = @"d:'TESTDATA'";
string[] files = Directory.GetFiles(sourceDirectory, "*.log",
SearchOption.TopDirectoryOnly);
var dictionary = new Dictionary<string, int>();
foreach (var file in files)
{
string[] lines = System.IO.File.ReadLines(file).Skip(4).ToArray();
foreach (var line in lines)
{
var elements = line.Split(' ');
if (elements.Length > 6)
{
if (dictionary.ContainsKey(elements[9]))
{
dictionary[elements[9]]++;
}
else
{
dictionary.Add(elements[9], 1);
}
}
}
}
Linqy应该做的事。怀疑它是否更有效率。而且,几乎可以肯定的是,它的调试更加麻烦。但是现在很流行:
static Dictionary<string,int> Slurp( string rootDirectory )
{
Dictionary<string,int> instance = Directory.EnumerateFiles(rootDirectory,"*.log",SearchOption.TopDirectoryOnly)
.SelectMany( fn => File.ReadAllLines(fn)
.Skip(4)
.Select( txt => txt.Split( " ".ToCharArray() , StringSplitOptions.RemoveEmptyEntries) )
.Where(x => x.Length > 9 )
.Select( x => x[9])
)
.GroupBy( x => x )
.ToDictionary( x => x.Key , x => x.Count())
;
return instance ;
}
一种更有效(性能方面)的方法是使用Parallel并行化外部foreach。Foreach方法。你还需要一个ConcurrentDictionary对象,而不是一个标准的字典。
不确定您是否正在寻找更好的性能或更优雅的代码。如果你喜欢函数式linq,可以这样写:
var query= from element in
(
//go through all file names
from fileName in files
//read all lines from every file and skip first 4
from line in File.ReadAllLines(fileName).Skip(4)
//split every line into words
let lineData = line.Split(new[] {' '})
//select only lines with more than 6 words
where lineData.Count() > 6
//take 6th element from line
select lineData.ElementAt(6)
)
//outer query will group by element
group element by element
into g
select new
{
Key = g.Key,
Count = g.Count()
};
var dictionary = list.ToDictionary(e=>e.Key,e=>e.Count);
结果是一个以单词为键并以单词重叠次数为值的字典。
我认为读取文件将是操作中最耗费精力的部分。在许多情况下,尝试在不同的线程上一次读取多个文件将损害而不是帮助性能,但是有一个线程除了读取文件什么都不做可能是有帮助的,这样它可以使驱动器尽可能繁忙。
如果文件变得很大(看起来很有可能),并且如果没有行超过32K字节(8,000-32,000个Unicode字符),我建议您以大约32K或64K字节(不是字符)的块读取它们。以字节读取文件并将其细分为行可能比以行读取要快,因为细分可能发生在与物理磁盘访问不同的线程上。
我建议从一个线程开始,一个线程用于磁盘访问,一个线程用于解析和计数,它们之间有一个阻塞队列。磁盘访问线程应该将包含32K字节数组的数据项放在队列中,指示有多少字节是有效的[可能在文件末尾少于32K],并指示它是否是文件的最后一条记录。解析线程应该读取这些项,将它们解析成行,并更新相应的计数。
为了提高计数性能,定义
可能会有所帮助class ExposedFieldHolder<T> {public T Value; }
则有一个Dictionary<string, ExposedFieldHolder<int>>
。必须为每个字典槽创建一个新的ExposedFieldHolder<int>
,但dictionary[elements[9]].Value++;
可能比dictionary[elements[9]]++;
快,因为后者语句翻译为dictionary[elements[9]] = dictionary[elements[9]]+1;
,并且在读取时必须查找一次元素,在写入时必须再次查找]。
如果有必要在多个线程上进行解析和计数,我建议每个线程都有自己的队列,并且磁盘读取线程在每个文件之后切换队列[文件的所有块应该由同一个线程处理,因为一个文本行可能跨越两个块]。此外,虽然可以使用ConcurrentDictionary
,但让每个线程拥有自己独立的Dictionary
并在最后合并结果可能会更有效。