针对大量可比较对象测试现有字符串的最佳方法

时间:2009-01-31 04:17:23

标签: c# regex string compare

假设你有一个定义一个值的首字母缩略词列表(例如AB1,DE2,CC3),你需要检查一个字符串值(例如“Happy:DE2 | 234”)以查看是否找到了一个首字母缩略词字符串。对于缩写词的简短列表,我通常会创建一个使用分隔符的简单RegEx(例如(AB1 | DE2 | CC3))并只查找匹配项。

但如果有超过30个首字母缩略词可以匹配,我将如何处理?使用相同的技术(丑陋)还是有更有效和优雅的方法来完成这项任务是否有意义?

请记住示例首字母缩略词列表和示例字符串不是我正在使用的实际数据格式,而只是表达我的挑战的一种方式。

顺便说一下,我读了一篇SO related question,但并不认为它适用于我想要完成的任务。

编辑:我忘记了我需要捕获匹配的值,因此选择使用正则表达式...

5 个答案:

答案 0 :(得分:3)

就我个人而言,我认为30对于正则表达式来说并不是特别大,所以我不会太快将其排除在外。您可以使用一行代码创建正则表达式:

var acronyms = new[] { "AB", "BC", "CD", "ZZAB" };
var regex = new Regex(string.Join("|", acronyms), RegexOptions.Compiled);
for (var match = regex.Match("ZZZABCDZZZ"); match.Success; match = match.NextMatch())
    Console.WriteLine(match.Value);
// returns AB and CD

因此代码相对优雅且可维护。如果您知道缩写词数量的上限,我会进行一些测试,谁知道已经在正则表达式引擎中内置了哪种优化。您还可以从未来的正则表达式引擎优化中免费获益。除非你有理由相信性能会成为问题,否则请保持简单。

另一方面,正则表达式可能有其他限制,例如默认情况下,如果您有缩写词AB,BC和CD,那么它只会返回其中两个作为“ABCD”中的匹配项。所以它很擅长告诉你有一个缩写,但是你需要注意捕捉多个匹配。

当性能成为我的问题(> 10,000项)时,我将'首字母缩略词'放在HashSet中,然后搜索文本的每个子字符串(从最小首字母缩写词长度到最大缩写词长度)。这对我来说没问题,因为源文本很短。我之前没有听说过,但是首先看一下你提到的问题中提到的Aho-Corasick算法,似乎是解决这个问题的更好的通用解决方案。

答案 1 :(得分:0)

如果首字母缩略词具有固定大小(如上例所示),您可以计算所有这些的哈希值(可以在每个应用程序生命周期内完成一次),然后将这些重叠的片段中的字符串拆分并为它们计算哈希值。然后你所要做的就是从一个数组中搜索另一个数组中的值。

你可能可以使用这些信息创建一个后缀/前缀树或类似的缩略词和搜索,维基百科中有很多算法可以做到这一点。

您还可以为每个首字母缩略词创建一个确定性自动机,但它与之前的方法非常相似。

答案 2 :(得分:0)

为什么不简单地拆分字符串并比较返回的列表?在这种情况下使用REGEX似乎是不必要的开销。我知道你的格式可能有所不同,但似乎你可以:

  • 根据“标题分隔符”拆分字符串,在您的情况下为冒号:
  • 取结果的后半部分,首字母缩略词字符串,并根据首字母缩略词分隔符将其拆分,在本例中为管道|
  • 最后,遍历新拆分的首字母缩略词列表,并将每个首字母缩写词与具有嵌套for循环的候选列表进行比较

编辑:如果您只需要知道字符串中是否存在特定的首字母缩略词或一组首字母缩略词,请使用.Search()方法而不是.Match()。

答案 3 :(得分:0)

正则表达式方法似乎足够高效和优雅。当然,在构建表达式时,您必须注意未转义的字符,或者由于复杂性或大小限制而无法编译它。

另一种方法是构造一个trie data structure来表示所有的首字母缩略词(这可能与正则表达式匹配器正在做的有些重复)。当您遍历字符串中的每个字符时,您将创建一个指向trie根的新指针,并将现有指针推进到相应的子节点(如果有)。当任何指针到达叶子时,你会得到一个匹配。

答案 4 :(得分:0)

这是我想出的。我很感激你能提出任何建设性的批评......

首先,创建一个包含我的每个首字母缩略词的枚举:

enum acronym
{ AB1,DE2,CC3 }

接下来,我创建一个enum的字符串数组:

string[] acronyms = Enum.GetNames(typeof(acronym));

最后,我遍历字符串数组并执行regex.match方法:

foreach (string a in acronyms)
{
    Match aMatch = Regex.Match(input, a.ToString(), RegexOptions.None);
    if (aMatch.Success)
    {
        ...<do something>...
        break;
    }
}

看到有什么不对吗?