我正在尝试优化大型文本文件(300-600mb)中字符串的搜索。使用我目前的方法,花费的时间太长了。
目前我一直在使用
IndexOf
来搜索字符串,但是为字符串的每一行建立索引所需的时间太长(20 秒)。
如何优化搜索速度?我已经尝试过
Contains()
,但这也很慢。有什么建议么?我正在考虑正则表达式匹配,但我认为这没有显着的速度提升。也许我的搜索逻辑有缺陷
示例
while ((line = myStream.ReadLine()) != null)
{
if (line.IndexOf(CompareString, StringComparison.OrdinalIgnoreCase) >= 0)
{
LineIndex.Add(CurrentPosition);
LinesCounted += 1;
}
}
您使用的强力算法在 O(nm) 时间内执行,其中 n 是正在搜索的字符串的长度,m 是您要查找的子字符串/模式的长度。您需要使用字符串搜索算法:
Boyer-Moore 是“标准”,我认为: http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm
包括莫里斯-普拉特: http://www.stoimen.com/blog/2012/04/09/computer-algorithms-morris-pratt-string-searching/
和 Knuth-Morris-Pratt: http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
但是,使用精心设计的正则表达式可能就足够了,具体取决于您要查找的内容。请参阅 Jeffrey 的 Friedl 的著作,掌握正则表达式,以获取有关构建高效正则表达式的帮助(例如,无回溯)。
您可能还想查阅优秀的算法文本。我偏爱 Robert Sedgewick 的 算法 的 各种化身([C|C++|Java] 中的算法)
不幸的是,我不认为直接用 C# 可以做很多事情。
我发现 Boyer-Moore 算法对于这项任务来说非常快。但我发现没有办法做到像
IndexOf
那么快。我的假设是,这是因为 IndexOf
是在手动优化的汇编程序中实现的,而我的代码是在 C# 中运行的。
您可以在文章Fast Text Search with Boyer-Moore中看到我的代码和性能测试结果。
您看过这些问题(和答案)吗?
如果您只想读取文本文件,那么按照现在的方式进行操作似乎是正确的方法。其他想法:
如果可以对数据进行预排序(例如将其插入文本文件时),那可能会有所帮助。
您可以将数据插入数据库并根据需要进行查询。
您可以使用哈希表
您可以使用regexp.Match(String)。正则表达式匹配速度更快。
静态无效Main()
{
string text = "One car red car blue car";
string pat = @"(\w+)\s+(car)";
// Instantiate the regular expression object.
Regex r = new Regex(pat, RegexOptions.IgnoreCase);
// Match the regular expression pattern against a text string.
Match m = r.Match(text);
int matchCount = 0;
while (m.Success)
{
Console.WriteLine("Match"+ (++matchCount));
for (int i = 1; i <= 2; i++)
{
Group g = m.Groups[i];
Console.WriteLine("Group"+i+"='" + g + "'");
CaptureCollection cc = g.Captures;
for (int j = 0; j < cc.Count; j++)
{
Capture c = cc[j];
System.Console.WriteLine("Capture"+j+"='" + c + "', Position="+c.Index);
}
}
m = m.NextMatch();
}
}
当我搜索带有 c# 标签的“搜索大文本文件”时,这篇文章是 stackoverflow 上最热门的文章。尽管问题仍然存在,但自这篇文章最初发布以来,有些事情已经发生了变化。比如 300-600 MB 不再被视为大文件,并且 System.Text.RegularExpressions.Regex 的性能得到极大提高。由于这些原因,我觉得更新答案是公平的。
简而言之,使用当前版本的 dotnet 中的
System.Text.RegularExpressions.Regex
对于您能想到的任何搜索都将非常快。速度真的变快了
从 .NET7 开始,Regex 根据实例化方式合并了 4 个不同的引擎。这些引擎提供了高度优化的搜索,“在许多情况下,除了最极端的情况外,它最终在所有情况下都明显优于 Boyer-Moore。”
使用
RegexOptions.Compiled
或 GeneratedRegex
的 4 个引擎将生成最快的代码(即最好的最佳情况性能)。对于大多数情况,这是一个很好的解决方案。
但是,如果您的用例需要最大稳定性或容易受到输入滥用的影响,那么使用
RegexOptions.NonBacktracking
将通过切换到基于有限自动机的引擎来提供“最佳最坏情况性能”,“以换取降低的最佳情况性能”这“保证它只会对输入中的每个字符执行摊销恒定量的工作。”
这里是 Stephen Toub 的完整博客,介绍了 .NET7 中正则表达式中添加的许多令人印象深刻的优化。
要通过并行性进一步提高
System.Text.RegularExpressions.Regex
的性能或处理超出 RAM 的文件,您可能还想看看 Gigantor。