【问题标题】:Best way to test for existing string against a large list of comparables针对大量可比对象测试现有字符串的最佳方法
【发布时间】:2010-10-04 15:15:50
【问题描述】:

假设您有一个定义值的首字母缩写词列表(例如 AB1、DE2、CC3),并且您需要检查一个字符串值(例如“Happy:DE2|234”)以查看是否在字符串。对于缩写词的简短列表,我通常会创建一个使用分隔符的简单正则表达式(例如 (AB1|DE2|CC3) ),然后查找匹配项。

但是,如果要匹配超过 30 个首字母缩写词,我将如何解决这个问题?使用相同的技术是否有意义(丑陋)还是有更有效和优雅的方式来完成这项任务?

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

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

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

【问题讨论】:

    标签: c# regex string compare


    【解决方案1】:

    我个人认为 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 算法似乎是解决这个问题的更好的通用解决方案。

    【讨论】:

    • 不错!一个问题...您提到匹配将包括两个匹配...不会只返回第一个匹配(我真正想要的),因为当 match.Success 满足时 for 循环结束?
    • 它在成功时循环,所以至少对我来说,它返回 AB 和 CD,但不返回 BC。如果您只想检查任何匹配项,则可以使用 if(regex.IsMatch(inpuut)){..}
    • doh...刚刚想通了...您不能使用 regex.Matches("ZZZABCDZZZ") 代替 for..loop 吗?无论如何,非常优雅的解决方案并回答了我的问题!谢谢!
    【解决方案2】:

    这是我想出的。如果您能提出任何建设性的批评意见,我将不胜感激......

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

    enum acronym
    { AB1,DE2,CC3 }
    

    接下来我创建一个枚举的字符串数组:

    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;
        }
    }
    

    看到有什么问题吗?

    【讨论】:

    • 也许你可以只使用 if(input.Contains(a)) 而不是 Regex?
    • 使用 .Contains 可能会运行得更快,但我需要捕获匹配值(抱歉,我刚刚更新了问题)...
    • 如果首字母缩写词只是字符串(不是正则表达式),则根据定义匹配的值等于您用来匹配的首字母缩写词,例如 if (input.Contains(a)) { matchedValue = a; ... }。
    【解决方案3】:

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

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

    【讨论】:

    • 啊!我只是看了看 trie 数据结构链接,现在我的脑痛了...不过说真的,我应该仔细看看它是否可以工作...谢谢您的建议!
    • 是的,这可能不值得努力,因为正则表达式真的要简单得多。
    【解决方案4】:

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

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

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

    【讨论】:

    • 我没有想过嵌套循环。我给出的示例字符串和首字母缩略词已启用;是存储首字母缩略词的许多不同格式的子集...因此拆分字符串不会像使用正则表达式那样有效...
    • 对不起,我的意思是我要搜索的字符串有很多不同的格式,所以我无法可靠地拆分字符串值...
    【解决方案5】:

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

    您可能可以从首字母缩略词中创建一个后缀/前缀树或类似的东西并使用此信息进行搜索,维基百科中有很多算法可以做到这一点。

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

    【讨论】:

    • 不幸的是,首字母缩略词没有固定的大小,所以我不认为哈希会有所帮助......但有趣的想法,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多