【问题标题】:What's the best way for such strings match?这种字符串匹配的最佳方式是什么?
【发布时间】:2009-12-12 02:54:39
【问题描述】:

我想分析消息的类型以获得最佳性能,消息以常量字符串开头,后跟一个空格。常量字符串属于一个已知的字符串数组列表,如 "CUT"、"GET"、"LOGIN" ...

所以我不喜欢重复 memcmp(data, "GET", 3) 对性能不利的事情。我想知道有没有更好的解决方案。也许我可以把这个常量字符串数组编译成一个DFA来快速匹配字符串,但是我不知道怎么做,还有其他更好的解决方案吗?

可以使用词法分析器来做到这一点吗?

【问题讨论】:

  • 您的意思是memcmpmemcpy 吗?

标签: c algorithm


【解决方案1】:

看看Ragel。并在Mongrel 供实际使用。虽然我发现 ragel 附带的邮件解析示例也是一个有趣的小示例。

不过,根据您的协议,仅检查第一个字节可能会让您进入单个后续 memcmp() 以验证您的动词确实是正确的。 “C”、“L”、“G”都是不同的值。

【讨论】:

  • @Andrew,在这里我没有列出完整的长字符串列表。 :)
  • @arsane:这就是我说“依赖”的原因——因为我对你的用例了解不多:-)
【解决方案2】:

如果常量字符串的数量有限,您可以手写自己的“dfa”。只需检查第一个字符。如果它是“c”并且数组中唯一以“c”开头的字符串是“CUT”,那么你可以提前中断,因为你已经完成了。如果有两个以 C 开头的字符串,请检查第二个字符等。如果有大量可能的字符串,这显然不是一个好的解决方案。

有一个可能是您想要的 GNU C 正则表达式库。我可能会建议使用更简单的语言(如 Perl 或 Python)学习更多关于正则表达式的知识,然后在您对 reg 感到满意时学习 C 库。一般的exe。

另外,我很困惑为什么你在谈论 memcpy。你是说memcmp吗?为什么要使用它而不是 strcmp?

【讨论】:

  • 正则表达式库可以解决这个问题。但我不确定这是一个轻量级的解决方案。对于 memcmp,我认为 strncmp 的这个问题没有太大区别。
  • 因为我认为 strcmp 会在第一个差异处短路。
【解决方案3】:

您可以做的一件事是让您的程序在启动时遍历您的命令字符串列表,并使用它们来构建查找树。然后在运行时,您可以通过向下导航树来进行有效的查找,在每个节点处根据字符串中的下一个字母选择下一个子节点,直到您到达叶节点(在这种情况下您有匹配项)或点击一个死胡同(下一个字母没有子节点),你知道没有匹配。

(构造树非常简单——它与查找算法几乎相同,除了当您没有找到下一个字母的子节点时,您创建一个并将其添加到当前节点,然后继续)

【讨论】:

    【解决方案4】:

    我喜欢这个应用程序的ternary search trees。查找时间为 O(m),其中 m 是输入字符串的长度。

    还有一篇关于这个数据结构的文章有点帮助here


    另一种方法

    如果您的字符串恰好是 4 个 ASCII 字符或更短,您可以将它们存储在 32 位整数中,然后使用 switch 语句进行恒定时间比较。如果您能够使用 64 位整数,那么您最多可以比较 8 个 ASCII 字符。

    将字符串的前 4 个字符表示为整数的函数如下所示:

    #include <inttypes.h>
    
    uint32_t str_as_int(const char* s) {
      uint32_t n = 0;
      int i;
      for (i = 0; s[i] != '\0' && i < sizeof(uint32_t); i++)
        n |= s[i] << (24 - i * 8);
      return n;
    }
    

    【讨论】:

    • 不错。我喜欢 len
    【解决方案5】:

    呃,是的,你在处理字符串时不应该使用 memcpy() 或任何 mem() 函数。为什么?字符串函数会考虑终止空字符,因为前者更多是字节的原始副本。处理字符串时始终使用 string.h 函数。

    【讨论】:

      【解决方案6】:

      我会首先使用您拒绝的非常简单的算法。首先让它工作,然后让它快速运行。如果我发现我真的需要专注于我系统的那个特定的微小部分进行优化,我会选择做一些几乎与显而易见的解决方案一样简单的事情,但要快一个数量级左右。

      想到的显而易见的事情是将一个候选字符串列表替换为 26 个候选字符串列表。您可能已经猜到了,26 是字母表中的字母数,现在候选列表中的每个字符串都以相同的字母开头。查看消息的第一个字母,并使用快速查找表来选择合适的候选列表。因此,如果您的第一个字母是“C”,请搜索候选列表 { "COPY", "CUT", "CLOSE" }。

      如果这还不够快,那么我会认真考虑一些重量级的解决方案,但我怀疑这种事情真正需要多久。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-02
        • 1970-01-01
        • 1970-01-01
        • 2015-11-16
        • 2017-07-31
        • 1970-01-01
        • 2013-12-11
        • 1970-01-01
        相关资源
        最近更新 更多