将结构与一组模式匹配

Posted

技术标签:

【中文标题】将结构与一组模式匹配【英文标题】:Match a structure against set of patterns 【发布时间】:2013-07-24 21:28:47 【问题描述】:

我需要将一个结构与一组模式进行匹配,并对每个匹配项采取一些措施。

模式应该支持通配符,我需要确定哪些模式与传入结构匹配,示例集:

action=new_user email=*
action=del_user email=*
action=* email=*@gmail.com
action=new_user email=*@hotmail.com

可以实时添加/删除这些模式。可能有数千个连接,每个都有自己的模式,我需要通知每个连接我收到了一个匹配的结构。模式不是完全正则表达式,我只需要用通配符 * 匹配一个字符串(简单匹配任意数量的字符)。

当服务器接收到结构 action=new_user email=testuser@gmail.com 的消息(我们称之为消息 A)并且我需要找出模式 1 和 3 与此消息匹配时,我应该对每个匹配的模式执行操作(发送此结构 A到相应的连接)。

如何以最有效的方式做到这一点?我可以迭代这些模式并一一检查,但我正在寻找更有效和线程安全的方法来做到这一点。可能可以对这些模式进行分组以减少检查。有什么建议可以做到吗?

UPD:请注意,我想匹配乘法模式(thousands),而不是固定的“字符串”(实际上是一个结构),反之亦然。换句话说,我想找出适合给定结构 A 的模式。

【问题讨论】:

正则表达式库?概率。最好的。 您能否澄清一下:首先,我看到 4 种模式,我认为 1 + 3 匹配(不是 1+2)。其次,您只需要一个是/否的答案(=至少有一个模式匹配),还是您需要一个答案,例如:模式 x,y 匹配,其余不是?第三,模式的集合是固定的,还是可以是任何集合?第四,您的模式语言是什么:完全匹配每个符号,但匹配 * 的任何字符串?还是更通用的正则表达式? 您的模式支持哪些类型的通配符?如果 * 是唯一的,那么您可以编写简单的循环来验证这种模式。如果您需要正则表达式,请使用正则表达式库。 @PSIAlt:模式 3 不应该也与您给出的示例匹配吗? @Zane 很抱歉这个错误。 1+3 应该匹配。是的,我需要对每个匹配模式采取一些行动(将此结构发送到相应的连接)。编辑帖子以说明这一点。 【参考方案1】:

将模式转换为正则表达式,并使用RE2 匹配它们,这是用 C++ 编写的,是最快的之一。

【讨论】:

这可能是问题解决方案的一部分 为了获得更高的速度,您可能需要创建一个正则表达式,例如(pattern1)|(pattern2)|... 匹配后,您可以检索匹配的模式。但是你只会得到其中一个:如果 pattern1 和 pattern2 都匹配,你只会得到 pattern1。 是的,但我需要找到所有匹配的模式并为每个模式采取行动(将数据发送到相应的套接字)【参考方案2】:

实际上,如果我理解正确的话,第四个模式是多余的,因为第一个模式更通用,并且包含了与第四个匹配的每个字符串。只剩下 3 种模式,可以通过这个函数轻松检查:

bool matches(const char* name, const char* email)

    return strstr(name, "new_user") || strstr(name, "del_user") || strstr(email, "@gmail.com");

如果您更喜欢解析整个字符串,而不仅仅是匹配 actionemail 的值,那么下面的函数应该可以解决问题:

bool matches2(const char* str)

    bool match = strstr(str, "action=new_user ") || strstr(str, "action=del_user ");
    if (!match)
    
        const char* emailPtr = strstr(str, "email=");
        if (emailPtr)
        
            match = strstr(emailPtr, "@gmail.com");
        
    
    return match;

请注意,您作为参数输入的字符串必须使用\0 进行转义。你可以阅读strstr函数here。

【讨论】:

对不起,我没有提到可以实时添加/删除模式。可能有数千个连接,每个都有自己的模式,我需要通知每个连接我已经收到了这个结构(这是匹配的)。【参考方案3】:

strglobmatch 仅支持*?

#include <string.h>  /* memcmp, index */

char* strfixstr(char *s1, char *needle, int needle_len) 
  int l1;
  if (!needle_len) return (char *) s1;
  if (needle_len==1) return index(s1, needle[0]);
  l1 = strlen(s1);
  while (l1 >= needle_len) 
    l1--;
    if (0==memcmp(s1,needle,needle_len)) return (char *) s1;
    s1++;
  
  return 0;


int strglobmatch(char *str, char *glob) 
  /* Test: strglobmatch("almamxyz","?lmam*??") */
  int min;
  while (glob[0]!='\0') 
    if (glob[0]!='*') 
      if ((glob[0]=='?') ? (str[0]=='\0') : (str[0]!=glob[0])) return 0;
      glob++; str++;
     else  /* a greedy search is adequate here */
      min=0;
      while (glob[0]=='*' || glob[0]=='?') min+= *glob++=='?';
      while (min--!=0) if (*str++=='\0') return 0;
      min=0; while (glob[0]!='*' && glob[0]!='?' && glob[0]!='\0')  glob++; min++; 
      if (min==0) return 1; /* glob ends with star */
      if (!(str=strfixstr(str, glob-min, min))) return 0;
      str+=min;
    
  
  return str[0]=='\0';

【讨论】:

对不起,我怀疑这种暴力方式是否足以每秒匹配多个模式 先测量再根据直觉判断。如果把strfixstr换成最优子串搜索(如Knuth--Morris--Pratt),strglobmatch的算法实际上是最优的(O(glob_len + str_len)),实现简单快速。 感谢回复,但我应该这样检查每个字符串的许多模式,所以它比O(glob_len + str_len)重得多【参考方案4】:

如果你想要的只是通配符匹配,那么你可以试试这个算法。关键是检查所有不是通配符的子字符串是否在字符串中。

patterns = ["*@gmail.com", "akalenuk@*", "a*a@*", "ak*@gmail.*", "ak*@hotmail.*", "*@*.ua"]
string = "akalenuk@gmail.com"
preprocessed_patterns = [p.split('*') for p in patterns]


def match(s, pp):
    i = 0
    for w in pp:
        wi = s.find(w, i)
        if wi == -1:
            return False
        i = wi+len(w) 
    return i == len(s) or pp[-1] == ''

print [match(string, pp) for pp in preprocessed_patterns]

但最好还是使用正则表达式,以防您将来需要的不仅仅是通配符。

【讨论】:

以上是关于将结构与一组模式匹配的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式

(原创)数据结构之利用KMP算法解决串的模式匹配问题

将多个 isinstance 检查转换为结构模式匹配

算法项集匹配模式

正则表达式——更多匹配模式

多个连接字符串的同步模式匹配算法