【问题标题】:C# Code/Algorithm to Search Text for Terms用于在文本中搜索术语的 C# 代码/算法
【发布时间】:2010-09-27 18:59:09
【问题描述】:

我们有 5mb 的典型文本(只是简单的文字)。我们有 1000 个单词/短语可用作在本文中搜索的术语。

在 .NET(最好是 C#)中最有效的方法是什么?

我们的想法包括正则表达式(一个,很多),甚至还有 String.Contains 的东西。

输入是 2mb 到 5mb 的文本字符串 - 所有文本。多次点击是好的,因为在每个匹配的术语(1000 个)中,我们确实想知道它。就整个执行时间而言的性能,不关心足迹。当前算法使用朴素的 string.contains 提供大约 60 秒以上的时间。我们不希望 'cat' 提供与 'category' 甚至 'cats' 的匹配(即整个词词必须命中,没有词干)。

我们预计文本中的命中率

【问题讨论】:

  • 您希望这些词多久出现一次?你需要你的结果是什么?你在乎多次点击吗?
  • 输入数据采用什么形式?我们是在解析文件、数据库、字符串吗?
  • 当然还有,你想要什么样的性能,你当前的算法给你什么样的性能?
  • 输入是 2mb 到 5mb 的文本字符串 - 所有文本。多次点击是好的,因为在每个匹配的术语(1000 个)中,我们想知道它。执行时间方面的性能,不关心足迹。当前算法使用朴素的 string.contains 给出大约 60 秒以上的时间。

标签: c# .net algorithm search


【解决方案1】:

一个幼稚的字符串。包含 762 个单词(最后一页)的 War and Peace (3.13MB) 对我来说运行大约 10 秒。切换到 1000 个 GUID 大约需要 5.5 秒。

Regex.IsMatch 在大约 0.5 秒内找到了 762 个单词(其中大部分可能也在早期页面中),并在 2.5 秒内排除了 GUID。

我建议你的问题出在其他地方......或者你只需​​要一些像样的硬件。

【讨论】:

  • 是的,我们最初的实现很糟糕,所以时代已经过去了。您更正 IsMatch 的工作速度非常快,但是如果您在多个术语上使用它,那么您需要知道哪些匹配,即我们使用 regex.Matches.Count 并且速度很慢,并且发现运行 regex.IsMatch() 更快一千次​​pan>
  • 更多细节在这里:stackoverflow.com/questions/382263/…
  • 另外,您的正则表达式可能有点快,因为您需要尝试查找单词边界,即 \bterm\b。 (为了避免在“类别”中找到“猫”作为第一个不匹配)。如果你这样做,你会发现正则表达式最多大约 6 秒(就像我猜的 GUID)。
【解决方案2】:

为什么要重新发明轮子?为什么不直接利用 Lucene.NET 之类的东西?

【讨论】:

  • 是的。这是个好建议。我们在其他地方使用 Lucene,所以我们可以这样做。我想这是输入源的大小和要查找的术语数量之间的平衡。多个正则表达式在达到一定大小时效果很好,然后像 lucene 这样的东西就变得值得了。
【解决方案3】:

您是否考虑过以下问题:

  1. 你关心子字符串吗?假设我正在寻找“猫”这个词,不多也不少。现在考虑 Knuth-Morris-Pratt 算法,或 string.contains 用于“连接”。这两个都将返回 true (或索引)。这样可以吗?

  2. 您还必须研究词干或“有限”状态的概念。让我们再找“日记”,试句是“日记有很多种”。对你和我来说,我们有“日记”这个词,这算吗?如果是这样,我们将需要预处理句子,将单词转换为有限状态(日记 -> 日记),句子将变为“有多种日记”。现在我们可以说Diary 在句子中(请看porter Stemmer Algroithm)

  3. 此外,在处理文本(也称为自然语言处理)时,您可以删除一些作为噪音的词,例如“a, have, you, I, me, some, to”被认为是无用的词,然后可以在任何处理发生之前被删除?例如

“我今天写了一些 C#”,如果我有 10,000 个关键作品要查找,我将不得不扫描整个句子 10,000 x 句子中的单词数。提前去除噪音会缩短处理时间

“今天写的 C#”

可以在此处找到有关 NLP 的精彩文章。 Sentance comparing

HTH

骨头

【讨论】:

  • 是的,我们要避免子字符串,即“类别”不应该与“猫坐”匹配,所以我们要处理词项。我们自己进行词干提取,所以这超出了这个搜索算法(但很好)。去除噪音更难,因为它将性能时间转移到“过滤器”,我们只做一次。
【解决方案4】:

修改后的Suffix tree 会非常快,虽然它会占用大量内存,而且我不知道构建它的速度有多快。然而,在那之后,每次搜索都需要 O(1)。

【讨论】:

  • 谢谢。不幸的是(在问题 cmets now 中,抱歉)我们的“输入文本”经常更改,因此建立索引将需要一段时间,即我们不需要再次在文本正文上重复新的搜索(使用这些术语)。跨度>
  • O(1) 是错误的。如果 m 是给定模式的长度,则搜索实际上需要 O(m)。只有直接索引(即 q-gram 索引)才能给出恒定的检索时间。
  • 是的,O(m),如果 m 是模式长度。但是 O(1) 如果 m 是必须检查匹配的字符串数。
【解决方案5】:

这是另一个想法:创建一个类似这样的类:

class Word
{
    string Word;
    List<int> Positions;
}

为文本中的每个唯一单词创建此类的一个实例。 Positions 数组将存储从找到该单词的文本开始的位置(以单词而非字符计数)。

然后再制作两个列表作为索引。一个将存储所有这些类按它们的文本排序,另一个 - 按它们在文本中的位置。本质上,文本索引可能是一个 SortedDictionary,而位置索引将是一个简单的List&lt;Word&gt;

然后要搜索一个短语,您将该短语拆分为单词。查找字典中的第一个单词(即 O(log(n)))。从那里你知道文本中跟随它的可能单词是什么(你从 Positions 数组中得到它们)。查看这些单词(使用位置索引在 O(1) 中找到它们)并继续,直到找到一个或多个完全匹配。

【讨论】:

  • '对于文本中的每个唯一单词,您都创建一个此类的实例' - 我不确定这会降低整个搜索时间的整体性能,不是吗需要一段时间来填充/制作?
  • 我不知道。您可以通过预先分配大量这些实例然后重用它们来提高整个事情的效率,这样以后就不会分配/释放太多内存了。
  • 另外,如果这对性能至关重要,为什么不尝试使用 C/C++ 或至少不安全的 C# 来执行此操作。指针魔法可以帮助加快这里的速度。
  • 此外,在 C# 中,您无法确定不安全指针是否比原生 String 对象快。
  • @JwJosefy - “文本索引可能是一个 SortedDictionary” - 这就是我的建议。
【解决方案6】:

您是要获得匹配单词的列表,还是要在文本中突出显示它们以获取匹配位置的开始和长度?如果您要做的只是找出单词是否存在,那么您可以使用子集理论来相当有效地执行此操作。

但是,我希望您尝试每个匹配项在文本中的开始位置...在这种情况下,这种方法将不起作用。

我能想到的最有效的方法是使用列表动态构建匹配模式,然后使用正则表达式。维护一个包含 1000 个项目的列表比维护基于这 1000 个项目的正则表达式模式要容易得多。

据我了解,Regex 使用建议的相同 KMP 算法来有效处理大量数据 - 因此,除非您真的需要深入了解它的工作原理(这可能对个人成长有益),否则也许正则表达式就可以了。

这里有一篇关于大文件中许多模式的搜索算法的非常有趣的论文:http://webglimpse.net/pubs/TR94-17.pdf

【讨论】:

  • 谢谢。对问题的评论以澄清一点(对不起,这里是新的)。实际上我们不需要这个位置,只需要子集 - 你可以扩展'然后你可以使用子集理论来相当有效地执行这个'吗?你的意思是只使用一个正则表达式语句一次还是 1000 个正则表达式?
  • @David:您需要从您的搜索词中找出哪些项存在,还是需要知道它们是否都作为一个集合存在?即“是的,我的所有搜索词都存在于我的文本中”还是只是“此文本包含我的搜索列表中的项目”?
  • 是的,我们需要“此文本包含我的搜索列表中的项目,它们都在这里”。
【解决方案7】:

这是瓶颈吗?多久时间? 5 MiB 实际上并不是要搜索的大量数据。正则表达式可能会很好,特别是如果您使用交替将所有搜索字符串编码为 one 模式。这基本上将搜索的总成本摊销到 O(n + m) ,其中 n 是文本的长度,m 是所有模式的总长度。请注意,这是一个非常好的性能。

非常适合许多模式的替代方法是Wu Manber algorithm。我已经发布了一个非常简单的算法C++ implementation

【讨论】:

  • 谢谢。我们正在尝试将所有术语 m 作为单个模式,尽管想知道在一定的模式大小之后它是否会降级(或者正则表达式会拒绝它,至少在 .NET 上)。
  • 后者实际上可能是真的,我不知道内部结构是否足以回答这个问题,抱歉。不过我对此表示怀疑。引擎似乎非常好。这种简单情况下的性能下降不应该发生,因为构造的表达式非常简单。再次警告购买者
  • 另外,请确保您是否有一个固定模式(例如具有 1000 个术语的大量交替正则表达式),您在任何循环之外声明 Regex 变量(最好也对其进行编译)。
【解决方案8】:

好的,目前的返工显示这是最快的(伪代码):

foreach (var term in allTerms)
{
    string pattern = term.ToWord(); // Use /b word boundary regex
    Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
    if (regex.IsMatch(bigTextToSearchForTerms))
    {                    
        result.Add(term);                                        
    }
}

令人惊讶的是(至少对我而言!)运行 1000 次正则表达式比使用 1000 个替代项的单个正则表达式更快,即“/b term1 /b | /b term2 /b | /b termN /b”然后尝试使用 regex.Matches.Count

【讨论】:

    【解决方案9】:

    相比之下,这表现如何?它使用 LINQ,所以可能会慢一些,不确定...

    List<String> allTerms = new List<String>(new String(){"string1", "string2", "string3", "string4"});
    List<String> matches = allTerms.Where(item => Regex.IsMatch(bigTextToSearchForTerms, item, RegexOptions.IgnoreCase));
    

    这使用经典谓词来实现 FIND 方法,所以它应该比 LINQ 更快:

    static bool Match(string checkItem)
    {
      return Regex.IsMatch(bigTextToSearchForTerms, checkItem, RegexOptions.IgnoreCase);
    }
    
    static void Main(string[] args)
    {
      List<String> allTerms = new List<String>(new String(){"string1", "string2", "string3", "string4"});
      List<String> matches = allTerms.Find(Match);
    }
    

    或者这使用 lambda 语法来实现经典谓词,它应该比 LINQ 更快,但比以前的语法更具可读性:

    List<String> allTerms = new List<String>(new String(){"string1", "string2", "string3", "string4"});
    List<String> matches = allTerms.Find(checkItem => Regex.IsMatch(bigTextToSearchForTerms, checkItem, RegexOptions.IgnoreCase));
    

    我没有对它们中的任何一个进行性能测试,但它们都使用正则表达式通过搜索列表实现了您的迭代想法。只是实现方式不同而已。

    【讨论】:

    • 好的,谢谢。我认为: List matches = allTerms.Where(item => Regex.IsMatch(bigTextToSearchForTerms, item, RegexOptions.IgnoreCase)); ..需要一个 IEnumerable 吗?不过我明白了 - 谢谢。
    • 另外,'Find' 的第三个示例需要是 FindAll'。我确实尝试了这些作为非正式测试,并且循环仍然边缘它,即来自 3mb 战争与和平的 642 个单词 = 循环 241 毫秒,allTerms.FindAll 283 毫秒 - 非常接近......
    猜你喜欢
    • 2018-08-09
    • 1970-01-01
    • 1970-01-01
    • 2017-08-29
    • 2023-03-17
    • 1970-01-01
    • 2014-11-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多