Ozan S. Yigit写的grep源码分析(不到1000行的正则表达式源码)


Ozan S. Yigit写的grep源码分析(不到1000行的正则表达式源码)
今天接着分析oz的grep源码。感觉他的写作风格我很喜欢,而且作者的代码也不长,写得非常流畅。佩服得不行,我原来也看过,但当时为了求快,有些地方就省过了,为了追求速度,只是为了看完,很多细致的地方就省略了,今天感觉,看源码,一定要看懂,如果没看懂,看再多的源码,对自己的帮助也不会太大。
昨天把gawk的源码大体框架弄懂了,今天就把grep的框架说一遍。
先读makefile文件,当时想调试,手工加debug,没弄出来,读grep.c时,看到注释中,说要用--DEBUG进行编译,于是打开makefile,发现有debug选项,于是
sudo make debug
这样,就编译出了,可以调试的版本。
再读grep.c文件
先调用re_comp进行编译,把正则表达式源码编译到nfa中。
再循环读文件,每读一行字串,就用re_exec进行匹配,如果匹配到了,就进行打印

再跳到regex.c中,分析re_comp
把"fo[1-3]"翻译成
chr f chr o clo 18bitset end
采用的方法是,逐字节扫描,用一个大的switch语句实现。
for(p=pat;*p;p++) {
    lp=mp;
    switch(*p){
        如果是.
        如果是^
        如果是$
        如果是[
            先处理[-1-2]中的第一个-
            再处理[]a]中的第一个]
            再处理[^ab]中的^
            处理[1-3]中的区间时,看程序时,要有动态的思想,自己在程序中执行,因为指向1,所以先执行else部分,指定增1后,指向-,所以再执行第一部分。这样就可以理解了。不然死活不理解,为什么c1要*(p-2)加1
                c1 = *(p-2) + 1;
                    c2 = *p++;
                    while (c1 <= c2)
                        chset((CHAR)c1++);
            
        如果是*
        如果是+
            其中把a+处理成aa*,因此,要先复制,再处理*
        如果是\\
            此处处理很有技巧,但难度不大。

    }
}
再就是re_exec(char *lp)进行字串的匹配了。
分三种情况,
1)'^ab' 开头是'^'只进行一次pmatch
2) 'xab' 先查找到'x',再调用pmatch,加快匹配速度
3) 其它 调用pmatch,如果匹配不上,就把字串向后移一个位置,再调pmatch,所以用了循环。

再分析pmatch函数
和re_comp一样,也是一个大的switch语句,对nfa的每个字符,控制了字串的匹配,所以是表达式为主导的匹配。
static char *
pmatch(char *lp, CHAR *ap)
{
    register int op, c, n;
    register char *e;        /* extra pointer for CLO */
    register char *bp;        /* beginning of subpat.. */
    register char *ep;        /* ending of subpat..     */
    char *are;            /* to save the line ptr. */

    while ((op = *ap++) != END)
        switch(op) {

        case CHR:
            if (*lp++ != *ap++)
                return 0;
            break;
        case CLO://这个地方我看了许多次才看明白。
            are = lp;
            switch(*ap) {

            case ANY:
                while (*lp)
                    lp++;
                n = ANYSKIP;
                break;
            case CHR:
                c = *(ap+1);
                while (*lp && c == *lp)
                    lp++;
                n = CHRSKIP;
                break;
            case CCL:
                while ((c = *lp) && isinset(ap+1,c))
                    lp++;
                n = CCLSKIP;
                break;
            default:
                re_fail("closure: bad nfa.", *ap);
                return 0;
            }

            ap += n;

            while (lp >= are) {
                if (e = pmatch(lp, ap))
                    return e;
                --lp;
            } //循环中,是什么意思呢?我想啊想,实在没招了,就读
Plodsoft的博客,作者呢,写到关键的位置,就结束了。后来才想,噢,如果以
'fo[a-z]*1' 匹配'foa1'吧,匹配到[a-z]*后,剩下的部分,再开一个pmatch进行匹配。
因为是匹配优先,如果用'fo[a-z]*1' 匹配字串'fo1',此时,要回退,所谓--lp,也许我还要用手工执行一次。
纸上得来终须浅,还是要手工推导。
因此,读程序时,先建立一个宏观的印象,非常重要。这就是我今天想说的话。


            return 0;

        }
}
 

猜你喜欢

转载自blog.csdn.net/woshiyilitongdouzi/article/details/87107485
今日推荐