静态字符串模式匹配模板库

  前段时间试着用Arduino UNO做一个串口通信检测仪,在报文检测这部分遇到了困难。通常应该是用正则表达式来做报文模式匹配。找来正则表达式库,是用跳转表实现的,需要2个256元素的整型数组,这就用掉了1K内存,而Arduino UNO一共才2K内存……坑爹呢?!!抓狂 于是只能尝试自己做模式匹配了。


一、概念验证

  正则表达式库都是动态构造有限自动机来做模式匹配,故而需要大量内存。而我的需求不需要动态构造,if判断即可。例如,要匹配“[a-z]{2}[0-9]{4}”这样的模式,可以将此正则表达式看作2段规则,“[a-z]{2}”和“[0-9]{4}”,每段都是一个字符判定和一个数量判定,可用级联if分段判定,代码如下:

const char* str = "a1bcd23456ef78"; //待检测字符串
size_t seg1=0, //“[a-z]{2}”段匹配计数
       seg2=0, //“[0-9]{4}”段匹配计数
       start=0; //匹配起点索引号
//遍历字符串
for (size_t index=0; str[index]; index++)
{
    //取当前字符
    char ch=str[index];
    if (seg1<2)
    {   //第一段数量不满
        if (ch>='a' && ch<='z')
        {   //第一段匹配成功,部分匹配
            cout<<ch<<" matching from "<<start<<endl;
            seg1++;
        }
        else
        {   //第一段不匹配
            cout<<ch<<" mismatched"<<endl;
            //复位
            seg1=0;
            seg2=0;
            start=index+1;
        }
    }
    else
    {   //第一段数量已满,匹配第二段
        if (seg2<4)
        {   //第二段数量不满
            if (ch>='0' && ch<='9')
            {   //第二段匹配成功
                seg2++;
                if (seg2==4)
                {   //第二段数量已满,匹配完成
                    cout<<ch<<" matched  from "<<start<<endl;
                    //复位
                    seg1=0;
                    seg2=0;
                    start=index+1;
                }
                else
                {   //第二段部分匹配
                    cout<<ch<<" matching from "<<start<<endl;
                }
            }
            else
            {   //第二段不匹配,平移回滚
                cout<<ch<<" slide and recheck"<<endl;
                //平移第一段
                seg1--;
                seg2=0;
                start++;
                index--;
            }
        }
    }
}

运行结果:

a matching from 0
1 mismatched
b matching from 2
c matching from 2
d slide and recheck
d matching from 3
2 matching from 3
3 matching from 3
4 matching from 3
5 matched  from 3
6 mismatched
e matching from 10
f matching from 10
7 matching from 10
8 matching from 10

  结果符合预期,相比正则表达式库,内存代价是n(规则段数量)个整型变量,且可以在字符串未完整获得的情况下逐字匹配,每次均可返回匹配结果。更长的正则表达式也可依此级联下去,但是将变得更加复杂,且难以复用此代码。


二、级联模板链

  在《Modern C++ Design》中介绍了一种模板技巧,可以将模板作为自己的参数,构造一个模板链,最后用部分特化模板来终结模板链即可。将此技巧应用于本例,代码如下:

typedef bool (*CharPredType)(const unsigned char);
typedef int  (*NumPredType) (const unsigned int);

template <CharPredType CharPred, CharPredType NumPred, typename RuleTail>
class LogicRule
{
public:
    LRResult operator()(unsigned char c)
    {
        //...
    }
};

class LogicRuleEnd;

template <CharPredType CharPred, CharPredType NumPred>
class LogicRule<CharPred, NumPred, LogicRuleEnd>
{
public:
    LRResult operator()(unsigned char c)
    {
        //...
    }
};

  通用版的LogicRule用于构造模板链,CharPred和NumPred是字符和数量的判定谓词,RuleTail是后续模式规则。部分特化版LogicRule用于终结模板链。LogicRuleEnd类仅有声明,用来指示模板链终结,不可用来定义对象。用这个模板链定义本例的正则表达式,代码如下:

LogicRule<Char<'a','z'>, Num<2>, LogicRule<Char<'0','9'>, Num<4>, LogicRuleEnd> > ruler;

  ruler是可用于匹配判定的函数对象,可以这样用:

using namespace std;
LogicRule<Char<'a','z'>, Num<2>, LogicRule<Char<'0','9'>, Num<4>, LogicRuleEnd> > ruler; //模式匹配逻辑尺
const char* str = "a1bcd23456ef78"; //待检测字符串
size_t start=0; //匹配起点索引号
//遍历字符串
for (size_t index=0; str[index]; index++)
{
    //取当前字符
    char ch=str[index];
    //模式匹配
    switch (ruler(ch))
    {
      case LRMatching:
        cout<<ch<<" matching from "<<start<<endl;
        break;
      case LRMatchAndContinue:
        cout<<ch<<" matched  from "<<start<<endl;
        break;
      case LRMatched:
        cout<<ch<<" matched  from "<<start<<endl;
        start=index+1;
        break;
      case LRMismatched:
        cout<<ch<<" mismatched"<<endl;
        start=index+1;
        break;
      case LRMismatchAndRecheck:
        cout<<ch<<" mismatched and recheck"<<endl;
        start=index;
        index--;
        break;
      case LRSlideAndRecheck:
        cout<<ch<<" slide and recheck"<<endl;
        start++;
        index--;
        break;
    }
}

运行结果:

a matching from 0
1 mismatched and recheck
1 mismatched
b matching from 2
c matching from 2
d slide and recheck
d matching from 3
2 matching from 3
3 matching from 3
4 matching from 3
5 matched  from 3
6 mismatched
e matching from 10
f matching from 10
7 matching from 10
8 matching from 10
  和手工编写代码相比,由于逻辑规则段落不能获知自己是否是第一段,因而第一段逻辑规则在非首字符匹配失败时都是回滚,后续逻辑规则匹配失败时才直接报告失败,略显冗余。如果阁下有更好的解决办法请赐教。

  完整模板库代码如下:

#ifndef LogicRuler_h
#define LogicRuler_h
//要支持面向对象调用,可将LogicRuler类里的方法声明为虚函数,并增加虚析构函数。代价是每段规则都增加了虚函数表指针的占用空间


/**************************************/
//匹配结果
enum LogicRulerResult
{
  LRMatching,          //部分匹配
  LRMatchAndContinue,  //匹配成功,且还可能有更长可匹配部分
  LRMatched,           //完全匹配成功,且匹配完成
  LRMismatched,        //完全不匹配
  LRMismatchAndRecheck,//不匹配, 但可从当前字符重新开始,当前字符需要回滚再匹配一次
  LRSlideAndRecheck    //不匹配, 但可将起点位置平移1位继续从当前字符开始,当前字符需要回滚再匹配一次 (此情况仅出现在第一段规则有数量上限时)
};


/**************************************/
//匹配单一字符谓词
template 
   
   
    
    
inline bool Char(const unsigned char c) { return c==SingleChar; }

//匹配范围字符谓词
template 
    
    
     
     
inline bool Char(const unsigned char c) { return c>=BeginChar&&c<=EndChar; }

//匹配所有可打印字符谓词
inline bool CharAllPrintable(const unsigned char c) { return Char<'\x20','\x7E'>(c); };

//匹配所有字母字符谓词
inline bool CharAllAlphabet(const unsigned char c) { return Char<'A','Z'>(c)||Char<'a','z'>(c); };

//匹配所有数字字符谓词
inline bool CharAllNumber(const unsigned char c) { return Char<'0','9'>(c); };

//匹配所有字母数字字符谓词
inline bool CharAllWord(const unsigned char c) { return CharAllAlphabet(c)||CharAllNumber(c); };

//匹配除指定单一字符外其它字符谓词
template 
     
     
      
      
inline bool CharExcept(const unsigned char c) { return !Char
      
      
       
       (c); }

//匹配除指定范围字符外其它字符谓词
template 
       
       
         inline bool CharExcept(const unsigned char c) { return !Char 
        
          (c); } /**************************************/ //匹配单一数量谓词 template 
         
           inline int Num(const unsigned int n) { return static_cast 
          
            (n-SingleNum); } //匹配范围数量谓词 template 
           
             inline int Num(const unsigned int n) { return n 
            
              EndNum?1:0; } //匹配上限数量谓词 template 
             
               inline int NumAtMost(const unsigned int n) { return n>SingleNum?1:0; } //匹配下限数量谓词 template 
              
                inline int NumAtLeast(const unsigned int n) { return n 
               
                 class LogicRule : public LogicRuler { protected: //后续规则链对象 RuleTail Tail; public: //复位 (必须手动调用基类复位函数) void Reset() { LogicRuler::Reset(); Tail.Reset(); } //匹配函数调用操作符 int operator()(const unsigned char c) { if (NumPredicate(Compared)<0) { //数量不满 if (CharPredicate(c)) { //匹配成功 return (Compared++,LRMatching); } else { //不匹配 return Compared?(Reset(),LRMismatchAndRecheck):(Reset(),LRMismatched); } } else { //数量满足,先匹配后续规则 switch (Tail(c)) { case LRMatching: //后续部分匹配 return LRMatching; case LRMatchAndContinue: //后续匹配成功且可延展 return LRMatchAndContinue; case LRMatched: //后续完全匹配成功 return (Reset(),LRMatched); case LRMismatched: //后续完全不匹配 if (NumPredicate(Compared+1)>0) { //本段数量已满,尝试平移,不可平移则完全不匹配 return Compared?(Compared--,LRSlideAndRecheck):(Reset(),LRMismatched); } else { //本段数量可延展 if (CharPredicate(c)) { //匹配成功 return (Compared++,LRMatching); } else { //不匹配 return Compared?(Reset(),LRMismatchAndRecheck):(Reset(),LRMismatched); } } case LRMismatchAndRecheck: case LRSlideAndRecheck: //后续回滚 return (Reset(),LRMismatchAndRecheck); } } } };//class LogicRule 
                
                  //特化逻辑规则类模板,用于终结规则链 //CharPredicate:字符匹配谓词 //NumPredicate :数量匹配谓词 template 
                 
                   class LogicRule 
                  
                    : public LogicRuler { protected: public: //复位 (必须手动调用基类复位函数) void Reset() { LogicRuler::Reset(); } //匹配函数调用操作符 int operator()(const unsigned char c) { if (CharPredicate(c)) { //匹配成功 Compared++; if (NumPredicate(Compared)<0) { //数量不满 return LRMatching; } else if (NumPredicate(Compared+1)>0) { //数量已满 return (Reset(),LRMatched); } else { //数量可延展 return LRMatchAndContinue; } } else { //不匹配 return Compared?(Reset(),LRMismatchAndRecheck):(Reset(),LRMismatched); } } };//class LogicRule 
                   
                     #endif 
                    
                   
                  
                 
                
               
              
             
            
           
          
         
       
      
      
     
     
    
    
   
   

PS:如果需要多态方式调用,可将LogicRuler类里的方法声明为虚函数,并且增加虚析构函数。这样就可以用LogicRuler引用或指针向函数传递逻辑尺对象。代价是每段规则都增加了虚函数表指针的占用空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值