• 最初步的正则表达式引擎:支持子表达式替换


    子表达式的语法为

    name:regex

    在处理时,会把name加入进符号表中,方法是hash,最简单的加法

    而在regex中如果碰到[name]这种模式,则尝试去找符号表,如果找不到则报错。

    代码如下,欢迎大家测试。

    #include <stdio.h>
    #include <malloc.h>
    #include <string.h>
    //这个版本允许定义正则子表达式,定义的时候以名字开头,然后是冒号,然后是正则表达式主体。
    //在引用子表达式的时候,需要用中括号把子表达式括起来,因此中括号也跟其他操作符一样,作为保留字符
    //目前还没有处理转义字符,再过几个版本吧。
    int token[100];
    int token_pointer;
    char reg_operator[100];
    int reg_operator_pointer=0;
    int name_number=0;
    //注意这里name_number是全局的,而其他的几个栈及变量都是每个子表达式都复用的
    //其实那些变量及栈可以每次申请一个,为了节省空间,我就懒得管了,直接复用。
    int input_pointer=0;
    char reg_input[40];//由于命名表达式的存在,考虑加长字符,其实随便多大都可以处理。
    enum reg_opera_type
    {
        parenthesis=1,
        kleen,
        cat,
        or,
        alias,
        literal_char
    };//正则节点类型 
    typedef struct _reg_pattern
    {
        enum reg_opera_type type;
        union
        {
            struct//对应二元操作符
            {
                int left;
                int right;
            };
            struct//对应假名
            {
                int origin_number;
                int hash_table_number;
            };
            char value;//对应字符值
            int sub;//对应闭包运算符和括号运算符
        };
    }reg_pattern;//差不多当作抽象语法树吧 
    reg_pattern reg_pattern_table[100];
    //当前的hash只使用加法hash加上一个模97,因为考虑到97是质数
    typedef struct _hash_table//hash表类型 
    {
        enum _state
        {
            empty=0,
            in_use,
            deleted
        }state;
        char* name_of_alias;
        int reg_pattern_number;
    }hash_table;
    hash_table simple_hash_table[100];
    int look_up_hash_table(char* source)//查询一个名字是否在hash表中
    {
        int string_length;
        int counter;
        int index;
        int result;
        string_length=strlen(source);
        result=0;
        for(index=0;index<string_length;index++)
        {
            result+=*(source+index);
        }
        result=result%97;
        counter=0;
        while(counter<97)//顶多查询97次,如果查询达到了97次,则说明当前hash表中不存在这个字符串
        {
            if(simple_hash_table[result].state==empty)//如果当前位置为空,说明不存在这个节点,直接返回-1
            {
                return -1;
            }
            else
            {
                if(simple_hash_table[result].state==deleted)//如果为删除状态,则继续向下寻找,注意索引溢出的处理
                {
                    result=(result+1)%97;
                    counter++;
                }
                else
                {
                    if(strcmp(source,simple_hash_table[result].name_of_alias)==0)//如果出于使用状态,则开始比较
                    {
                        return result;//名字相同则返回索引
                    }
                    else//不同则继续向下寻找
                    {
                        result+=(result+1)%97;
                        counter++;
                    }
                }
            }
        }
        return -1;//如果找了达到了97次,则返回-1
    }
    
    
    
    
    int insert_hash_table(char* source,int index_of_reg_pattern)
    {
        int string_length;
        int counter;
        int index;
        int result;
        string_length=strlen(source);
        result=0;
        for(index=0;index<string_length;index++)
        {
            result+=*(source+index);
        }
        result=result%97;
        counter=0;
        while(counter<97)
        {
            if(simple_hash_table[result].state==in_use)//如果在使用中,则继续下次寻找 
            {
                result=(result+1)%97;
                counter++;
            }
            else//如果可用,则使用 
            {
                simple_hash_table[result].state=in_use;
                simple_hash_table[result].name_of_alias=source;
                simple_hash_table[result].reg_pattern_number=index_of_reg_pattern;
                return result;
            }
        }
        return -1;//已经满了 ,插入失败 
    }
    
    int is_begin_of_token()
    //判断输入字符是否可以当作一个token的开始符号,这样是为了处理非显示的连接符
    {
        switch(reg_input[input_pointer])
        {
        case '*':
        case '|':
        case '.':
        case ']':
        case ')':
        case '':
             return 0;
        default: 
            return 1;
        }
        //这里两个闭括号的存在都是为了处理非显示的连接符
        //而把开括号移动到外部的switch之中
        //主要是为了防止括号嵌套引起的错误
    }
    tackle_invisible_cat()//如果后面跟的是开括号(包括普通括号和假名括号)或者字符,则这时候要去处理隐式连接符
    {
        if(is_begin_of_token())
        {
            tackle_cat();
        }
    
    }
        
    void tackle_or()//处理并操作符,这个处理完之后,输入指针也会后移
    {
        if(reg_operator_pointer!=0)//如果操作符栈中已经有操作符了
        {
            if(reg_operator[reg_operator_pointer-1]!='(')//括号另外说
            {
                name_number++;
                if(reg_operator[reg_operator_pointer-1]=='.')
                    //如果前面的优先级比当前的高,则处理前面的优先级
                {
                    printf("name%d is concat of name%d and name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);
                }
                else
                    //这里处理的是相同优先级的情况,其实这里可以与前面的合并的,只不过打印信息不同
                {
                    printf("name%d is  name%d or name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);
                }
                token[token_pointer-2]=name_number;
                token_pointer--;
                reg_operator[reg_operator_pointer-1]='|';
                input_pointer++;
            }
            else//对于括号,则直接入栈
            {
                reg_operator[reg_operator_pointer++]='|';
                input_pointer++;
            }
        }
        else//对于空操作符栈,也是直接入栈
        {
            reg_operator[reg_operator_pointer++]='|';
            input_pointer++;
        }
    }
    int tackle_cat()//处理连接符,处理完之后输入指针不会移动,因为指针本来就指向了后面的字符
    {
        if(reg_operator_pointer!=0)//如果操作符栈不为空
        {
            if(reg_operator[reg_operator_pointer-1]=='(')//如果前面有括号,则直接入栈
            {
                reg_operator[reg_operator_pointer++]='.';
            }
            else//对于前面不是括号的情况下
            {
                if(reg_operator[reg_operator_pointer-1]=='.')//优先级相同则输出前面的那个
                {
                    name_number++;
                    printf("name%d is the concat of name%d and name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);
                    reg_pattern_table[name_number].type=cat;
                    reg_pattern_table[name_number].left=token[token_pointer-2];
                    reg_pattern_table[name_number].right=token[token_pointer-1];
                    token[token_pointer-1]=0;
                    token[token_pointer-2]=name_number;
                    token_pointer--;
                }
                else//否则的话,前面的优先级比当前优先级低,操作符入栈
                {
                    reg_operator[reg_operator_pointer++]='.';
                }
            }
        }
        else//如果操作符栈为空,则入栈
        {
            reg_operator[reg_operator_pointer++]='.';
        }
    }
    void tackle_parenthesis(void)//处理闭括号模式,这里有点复杂,这里匹配完之后,指针会向后移一位
    {
        if(reg_operator[reg_operator_pointer-1]=='(')//如果前面那个操作符为开括号,则匹配输出,并把输入指针向后移
        {
            name_number++;
            printf("name%d is (name%d)
    ",name_number,token[token_pointer-1]);
            reg_pattern_table[name_number].type=parenthesis;
            reg_pattern_table[name_number].sub=token[token_pointer-1];
            token[token_pointer-1]=name_number;
            input_pointer++;
            reg_operator[--reg_operator_pointer]='';
            //这时候需要考虑后面的是否少了显示的连接符,如果判断缺少连接符,则需要加上去
            tackle_invisible_cat();
        }
        else//如果闭括号前面还有运算符,那么根据他们的优先级输出,这个时候输入指针是不变的,注意
        {
            name_number++;
            if(reg_operator[reg_operator_pointer-1]=='.')
            {
                printf("name%d is the concat of name%d and name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);
                reg_pattern_table[name_number].type=cat;
                reg_pattern_table[name_number].left=token[token_pointer-2];
                reg_pattern_table[name_number].right=token[token_pointer-1];
            }
            else
            {
                printf("name%d is  name%d or name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);
                reg_pattern_table[name_number].type=or;
                reg_pattern_table[name_number].left=token[token_pointer-2];
                reg_pattern_table[name_number].right=token[token_pointer-1];
            }
            token[token_pointer-1]=0;
            token[token_pointer-2]=name_number;
            token_pointer--;
            reg_operator_pointer--;
        }
    }
    int tackle_alias(void)//注意这里处理完中括号匹配之后,还需要处理可能存在的非显示连接符
    {
        int length,index;
        char* new_alias;
        index=length=0;
        while(reg_input[input_pointer+length]!=']')
        {
            length++;
        }
        new_alias=malloc(sizeof(char)*(length));//总的长度为length-1,另外加上一个。
        for(index=0;index<length-1;index++)
        {
            new_alias[index]=reg_input[input_pointer+index+1];
        }
        new_alias[index]='';
        input_pointer+=length+1;
        index=look_up_hash_table(new_alias);//寻找hash表
        if(index!=-1)
        {
            length=simple_hash_table[index].reg_pattern_number;//找到别名所代表的正则模式
            token[token_pointer++]=length;//别名的正则模式的名字入栈
            printf("match a alias,%s is the alias of name%d
    ",new_alias,length);
            tackle_invisible_cat();
            return 1;
        }
        else//否则,返回错误信息
        {
            printf("the alias can't be matched
    ");
            return -1;
        }
    }
    
    int tackle_inter_reg(void)
    {
        token_pointer=0;
        while(reg_input[input_pointer]!='')//还没处理到输入的末尾
        {
            switch(reg_input[input_pointer])
            {
            case '('://对于括号,直接入栈
                reg_operator[reg_operator_pointer++]='(';
                input_pointer++;
                break;
            case ')':
                tackle_parenthesis();
                break;
            case '*'://由于这个运算符优先级很高,不需要入栈了,直接拿顶上的token输出就行
                name_number++;
                printf("name%d is multiple of name%d
    ",name_number,token[token_pointer-1]);
                reg_pattern_table[name_number].type=kleen;
                reg_pattern_table[name_number].sub=token[token_pointer-1];
                token[token_pointer-1]=name_number;
                input_pointer++;
                tackle_invisible_cat();
                break;
            case '['://对于开的中括号,因为中括号中只能允许假名存在,所以直接调用函数处理,
                    //处理完之后指针位于闭中括号的后面一个字符,所以switch中不需要考虑闭中括号了
                    //注意处理时后面可能接上一个非显示的连接符
                    //这个时候需要特殊处理
                tackle_alias();
                break;
            case '|'://对于这个符号,
                tackle_or();
                break;
            default: 
                name_number++;
                printf("name%d is %c
    ",name_number,reg_input[input_pointer]);
                token[token_pointer++]=name_number;
                reg_pattern_table[name_number].type=literal_char;
                reg_pattern_table[name_number].value=reg_input[input_pointer];
                input_pointer++;
                tackle_invisible_cat();
                break;
            }
        }
        //处理完了输入,这个时候可能还有剩余的内容在栈中
        while(reg_operator_pointer>=1)//如果全部的输入都弄完了,可是 操作符栈中还有数据,则输出 
        {
            name_number++;
            if(reg_operator[reg_operator_pointer-1]=='.')
            {
                printf("name%d is concat of name%d and name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);    
                reg_pattern_table[name_number].type=cat;
                reg_pattern_table[name_number].left=token[token_pointer-2];
                reg_pattern_table[name_number].right=token[token_pointer-1];
    
            }
            else
            {
                printf("name%d is name%d or name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);
                reg_pattern_table[name_number].type=or;
                reg_pattern_table[name_number].left=token[token_pointer-2];
                reg_pattern_table[name_number].right=token[token_pointer-1];
    
            }
            token[token_pointer-2]=name_number;
            token_pointer--;
            reg_operator_pointer--;
        }
        return name_number;//返回最后一个生成的token,作为假名的正则代号
    }
    int tackle_particle()//这里处理一个正则表达式名字的定义,每次处理完之后复用输入数组
    {
        //首先处理假名部分
        int index;
        char* name;
        input_pointer=0;
        while(reg_input[input_pointer++]!=':')//注意这里在停止迭代之后,指针指的是冒号后面的那个字符,
        {
            //一直向后找,直到找到冒号分隔符
        }
        //字符串长度比指针的值小1
        name=malloc(sizeof(char)*(input_pointer));//考虑到字符串的结尾,于是不需要减1了
        for(index=0;index<input_pointer-1;index++)//把名字复制过去
        {
            name[index]=reg_input[index];
        }
        name[index]='';
        index=tackle_inter_reg();//处理当前的小正则表达式,并得到这个表达式所生成的名字
        insert_hash_table(name,index);
        printf("creat a alias: %s is name%d
    ",name,index);//输出,建立了一个新的假名
    }
    
    int main(void)
    {
        int index;
        printf("please type in your short regex definition
    ");
        printf("if there is no more definition, just enter a newline
    ");
        while(scanf("%s",reg_input))
        {
            tackle_particle();
            printf("please type in your short regex definition
    ");
            printf("if there is no more definition, just enter a newline
    ");
        }
        for(index=0;index<97;index++)
        {
            if(simple_hash_table[index].state==in_use)
            {
                printf("there is a alias whose name is %s
    ",simple_hash_table[index].name_of_alias);
                free(simple_hash_table[index].name_of_alias);
            }
        }
    }
  • 相关阅读:
    浓缩版java8新特性
    restful的认识和用法
    常用业务返回对象类ResponseJson
    微信小程序使用websocket通讯的demo,含前后端代码,亲测可用
    完整且易读的最新版小程序登录态和检验注册过没的app.js写法
    完整且易读的微信小程序的注册页面(包含倒计时验证码、获取用户信息)
    BCD工具类(8421)
    IDEA下使用protobuf2(java)
    chrome 调试技巧
    encodeURI和encodeURIComponent的区别?
  • 原文地址:https://www.cnblogs.com/huangfeidian/p/3150063.html
Copyright © 2020-2023  润新知