/*
这个小程序实现的是,匹配所有的子串。
*表示重复>=0次
.表示匹配任意字符
?表示重复0或1次
+表示重复>=1次
\加在上述字符前表示视作普通字符而非重复模式字符
*/
struct POS{
char * startIndex;//存放匹配子串在源串中起始索引char * endIndex;//匹配子串在源串中下一次匹配的起始位置,也就是子串结尾的下一个字符的位置
};
int g_repeatCount = 0;//记录重复数目的比如aaaabcd,那么a的重复数为4,主要处理类似a*这样的情况
char * match_start(char c, char * regexp, char * text, bool isAnyChar = false );//isAnyChar用于处理 ’.‘ 作为普通的点号的情况
char * match(char *regexp,char*text){//核心分派函数
if(regexp[0] == '\0') return text;
if(regexp[0] == '?' || regexp[0] == '+' || regexp[0] == '*') return NULL;//处理类似"*abd*ef"或者"a?*"这样重复模式字符是单独开头而不是跟着字符后面凑成一对重复匹配模式
if(regexp[0] == '\\' && ( regexp[1] == '*' || regexp[1] == '+' || regexp[1] == '?' || regexp[1] == '.') ){//匹配反斜杠,星号,等作为普通字符情况
if(text[0] == regexp[1]) return match(regexp + 2, text + 1);
else return NULL;
}
//需要联合多个字符处理的都放到前面处理,比如a*, c?这样的必须联合两个一块处理,其他的一般情况就是单个字符处理,放到后面,这样递归时的取正则的字符的时候,会优先按照特殊情况的处理(比如*),如果不是特殊情况才会继续走下去,按照一般处理
if(regexp[1] == '+') return text[0] == regexp[0] ? match_start(regexp[0], regexp+2, text + 1, regexp[0] == '.') : NULL;
char * tmp = NULL;
if(regexp[1] == '?') return text[0] != regexp[0] && regexp[0] != '.' ? match(regexp + 2, text) : //处理a?以及.?这样的情况
(tmp = match(regexp + 2, text + 1)) != NULL ? tmp :
(tmp = match(regexp + 2, text)) != NULL ? tmp :
NULL;
;
if(regexp[0] == '.' && text[0] != '\0') return match(regexp+1,text+1);
if(regexp[0] == text[0] ) return match(regexp+1, text+1);
return NULL;
}
char * match_start(char c, char * regexp, char * text, bool isAnyChar){//处理重复匹配* 的情况
char * lastRepeat = text;
if(isAnyChar && c != '.') return NULL;
while(c == *lastRepeat || isAnyChar && *lastRepeat != '\0') ++lastRepeat;
int save = lastRepeat - text;
char * tmp;
do{
if( (tmp = match(regexp, lastRepeat)) != NULL) return tmp;
--lastRepeat;//贪婪模式的匹配,返回的是所有匹配模式中,左侧的匹配的重复字符尽可能多的情况(如果左侧*可以匹配更多的字符而不导致剩余的模式无法完全匹配,那么就取左侧匹配的尽可能多的这种情况来提取子串)
}while(lastRepeat >= text);
g_repeatCount = save;//g_repeatCount只在这里被设置,调用者每次搜索字符串时会把它置为0
return NULL;
}
POS searchOne(char * reg, char * text){
POS pos = {NULL, NULL};
if(!reg || !text || * reg == '\0' || *text == '\0') return pos;
while(*text != '\0'){
pos.endIndex = match(reg, text);
if(pos.endIndex != NULL){
pos.startIndex = text;
break;
}
text += g_repeatCount + 1;//如果正则式中第一个重复模式无论重复匹配多少个字符,都导致后续的正则式依然无法匹配成功,那么,说明下一次匹配的起始位置,至少应该是g_repeatCount + 1。比如dda*ef 匹配“ddaaaegkk",匹配a*时,无论怎样a*后面的ef都是匹配失败,那么我们下次匹配正则式应该右移至少3+1=4个位置,也就是下次开始匹配的是"aegkk"而非"daaaegkk"
g_repeatCount = 0;
}
return pos;
}