C# 如何使用LINQ使C‘grep’更实用?
我有一个方法,它使用可枚举的搜索字符串跨文件执行简单的“grep”。实际上,我正在做一个非常简单的查找所有引用的过程C# 如何使用LINQ使C‘grep’更实用?,c#,linq,c#-3.0,functional-programming,C#,Linq,C# 3.0,Functional Programming,我有一个方法,它使用可枚举的搜索字符串跨文件执行简单的“grep”。实际上,我正在做一个非常简单的查找所有引用的过程 IEnumerable<string> searchStrings = GetSearchStrings(); IEnumerable<string> filesToLookIn = GetFiles(); MultiMap<string, string> references = new MultiMap<string, string&
IEnumerable<string> searchStrings = GetSearchStrings();
IEnumerable<string> filesToLookIn = GetFiles();
MultiMap<string, string> references = new MultiMap<string, string>();
foreach( string fileName in filesToLookIn )
{
foreach( string line in File.ReadAllLines( fileName ) )
{
foreach( string searchString in searchStrings )
{
if( line.Contains( searchString ) )
{
references.AddIfNew( searchString, fileName );
}
}
}
}
另一个希望这次保留文件名:
var filesWithLines =
filesToLookIn
.Select(f => new { FileName = f, Lines = File.ReadAllLines(f) });
var matchingSearchStrings =
searchStrings
.Where(ss => filesWithLines.Any(
fwl => fwl.Lines.Any(l => l.Contains(ss))));
但我似乎仍然丢失了我需要的信息
也许我只是从错误的角度来看待这个问题?从性能的角度来看,循环的执行顺序应该与原始示例大致相同
关于如何在更紧凑的函数表示中实现这一点,有什么想法吗?我将使用FindFile FindFirstFileEx、FindNextFile等API调用在文件中查找您正在搜索的术语。它可能比你一行一行地阅读要快
但是,如果这对你不起作用,你应该考虑创建一个IQueDaby实现,它将读取文件中的行并在读取它们时产生它们,而不是将它们全部读入数组。然后,您可以查询每个字符串,并仅在需要时获取下一个字符串
这会为你节省很多时间 请注意,在.NET 4.0中,许多从文件或搜索文件返回行的IO API将返回IEnumerable实现,这些实现与上面提到的完全相同,因为它将搜索目录/文件,并在适当时生成它们,而不是预先加载所有结果。var matches =
from fileName in filesToLookIn
from line in File.ReadAllLines(fileName)
from searchString in searchStrings
where line.Contains(searchString)
select new
{
FileName = fileName,
SearchString = searchString
};
foreach(var match in matches)
{
references.AddIfNew(match.SearchString, match.FileName);
}
编辑:
从概念上讲,查询将每个文件名转换为一组行,然后将该组行交叉连接到一组搜索字符串,这意味着每一行都与每个搜索字符串配对。该集合将过滤为匹配行,并选择每行的相关信息
multiple-from子句类似于嵌套的foreach语句。每一个都表示前一个迭代范围内的新迭代。多个from子句转换为该方法,该方法从每个元素中选择一个序列,并将结果序列展平为一个序列
C的所有查询语法都转换为扩展方法。然而,编译器确实使用了一些技巧。一种是使用匿名类型。当2+范围变量在同一范围内时,它们可能是幕后匿名类型的一部分。这允许任意数量的作用域数据流经扩展方法,如Select和Where,它们具有固定数量的参数。有关更多详细信息,请参阅
以下是上述查询的扩展方法翻译:
var matches = filesToLookIn
.SelectMany(
fileName => File.ReadAllLines(fileName),
(fileName, line) => new { fileName, line })
.SelectMany(
anon1 => searchStrings,
(anon1, searchString) => new { anon1, searchString })
.Where(anon2 => anon2.anon1.line.Contains(anon2.searchString))
.Select(anon2 => new
{
FileName = anon2.anon1.fileName,
SearchString = anon2.searchString
});
我不知道你可以像那样使用多个from语句。这到底是怎么回事?我对LINQ的体验完全是通过lambdas和扩展方法。这会转换为链式扩展方法吗?是的,多个from子句转换为对SelectMany扩展方法的调用。在Reflector中查看它,看看到底发生了什么。这就是我想要的,并且更好地表达了这个想法。谢谢你给SelectMany的提示,我不知道那里有。但是,我遇到了一个明显的性能差异:嵌套的“ForEach”需要229秒才能执行;LINQ嵌套“from”需要504秒。这是由于幕后的匿名对象创建/破坏造成的吗?我应该创建一个新的SO问题来解决这个问题吗?可能是。提出一个问题不会有什么坏处。
var matches = filesToLookIn
.SelectMany(
fileName => File.ReadAllLines(fileName),
(fileName, line) => new { fileName, line })
.SelectMany(
anon1 => searchStrings,
(anon1, searchString) => new { anon1, searchString })
.Where(anon2 => anon2.anon1.line.Contains(anon2.searchString))
.Select(anon2 => new
{
FileName = anon2.anon1.fileName,
SearchString = anon2.searchString
});