我想分析消息的类型以获得最佳性能,消息以常量字符串开头,后跟一个空格。常量字符串属于一个已知的字符串数组列表,如“CUT”,“GET”,“LOGIN”......
所以我不喜欢memcmp(数据,“GET”,3)反复出现这对性能有害的事情。我想知道有没有更好的解决方案。也许我可以将这个常量字符串数组编译成DFA以进行快速字符串匹配,但我不知道该怎么做,还有其他更好的解决方案吗?
可能使用lexer来做到这一点?
答案 0 :(得分:2)
看看Ragel。在Mongrel进行实际使用。虽然我发现用ragel附带的邮件解析示例也是一个有趣的小实验,但也是如此。
但是,根据你的协议,只需检查第一个字节就可以让你进入一个后续的memcmp()只是为了验证你的动词确实是正确的。 'C','L','G'都是不同的值。
答案 1 :(得分:1)
如果有一定数量的常量字符串,你可以手写自己的“dfa”。只需检查第一个字符。如果它是'c'并且数组中以'c'开头的唯一字符串是“CUT”,那么你可以提前中断,因为你已经完成了。如果有两个以C开头的字符串,请检查第二个字符等。如果有大量可能的字符串,这显然不是一个好的解决方案。
有一个GNU C正则表达式库可能就是你想要的。我可能会建议用更简单的语言(如Perl或Python)学习正则表达式,然后在熟悉reg时学习C库。一般来说。
另外,我很困惑为什么你在谈论memcpy。你的意思是memcmp吗?你为什么要用它而不是strcmp?
答案 2 :(得分:1)
您可以做的一件事是让您的程序在启动时迭代命令字符串列表,并使用它们来构建查找树。然后在运行时,您可以通过向下导航树来执行高效查找,在每个节点根据字符串中的下一个字母选择下一个子节点,直到您到达叶节点(在这种情况下,您有匹配)或命中一个死胡同(下一个字母没有子节点),你知道没有匹配。
(构建树很容易 - 它与查找算法的算法几乎相同,只是当你没有找到下一个字母的子节点时,你创建一个并将它添加到当前节点,然后继续)
答案 3 :(得分:1)
我喜欢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;
}
答案 4 :(得分:0)
嗯,是的,在处理字符串时你不应该使用memcpy()或任何mem()函数。为什么?井字符串函数会考虑终止空字符,因为前者更多是字节的原始副本。处理字符串时总是使用string.h函数。
答案 5 :(得分:0)
我首先会使用您拒绝的非常简单的算法。首先让它工作,然后快速。如果我发现我真的需要专注于系统中那个特殊的微小部分进行优化,我会选择做一些几乎与显而易见的解决方案一样简单的事情,但是要快一个数量级。
我想到的显而易见的事情就是替换一个候选字符串列表,比如26个候选字符串列表。您可能已经猜到,26是字母表中的字母数,而候选列表中的每个字符串现在都以相同的字母开头。查看消息的第一个字母,并使用快速查找表来选择适当的候选列表。因此,如果您的第一个字母为“C”,请搜索候选列表{“COPY”,“CUT”,“CLOSE”}。
如果这还不够快,那么我会认真对待一些重量级的解决方案,但我对这种事情经常需要的频率持怀疑态度。