• 最初步的正则表达式引擎:增加了字符集表示和?运算符和+运算符


    关于正则的语法方面,就做到这吧。下一步是生成nfa,进而dfa,以及dfa的最简化。等下一个版本出来的话,估计要好久了。

      1 #include <stdio.h>
      2 #include <malloc.h>
      3 #include <string.h>
      4 //这个版本在上一个版本的基础上,添加了?+这两个操作符,这两个操作符都是单目操作符,跟*操作符同一级别
      5 //因此碰到这两个操作符的时候,跟*操作符一样的处理
      6 //除了新加的操作符外,还添加了字符集,模式为a-z这种,但是字符集只能通过假名来引用
      7 //因此如果想使用字符集,则必须先通过假名来定义这个字符集,然后在后续的正则表达式中通过假名
      8 //来使用这个字符集
      9 //因此这里需要修改reg_opera_type,以及输入处理函数。
     10 //注意这里的字符集定义的识别是通过判断正则的第二个字符是否是-来作用的,所以如果在其他类型的正则
     11 //表达式中想使用-符号的话,千万不要把它放在第二个字符里面,因为这样会产生错误
     12 //虽然可以去判断正则的长度来消除这种错误,不过我就懒得管了
     13 //想改的话,读者自己去改吧
     14 int token[100];
     15 int token_pointer;
     16 char reg_operator[100];
     17 int reg_operator_pointer=0;
     18 int name_number=0;
     19 //注意这里name_number是全局的,而其他的几个栈及变量都是每个子表达式都复用的
     20 //其实那些变量及栈可以每次申请一个,为了节省空间,我就懒得管了,直接复用。
     21 int input_pointer=0;
     22 char reg_input[40];//由于命名表达式的存在,考虑加长字符,其实随便多大都可以处理。
     23 enum reg_opera_type
     24 {
     25     parenthesis=1,//对应括号
     26     closure,//对应闭包运算符
     27     one_or_more,//对应+运算符
     28     maybe_exist,//对应?运算符
     29     cat,//对应连接运算符
     30     or,//对应选择运算符
     31     alias,//对应命名子表达式
     32     set_of_char,//对应字符集
     33     literal_char//单字符
     34 };//正则节点类型 
     35 typedef struct _reg_pattern
     36 {
     37     enum reg_opera_type type;
     38     union
     39     {
     40         struct//对应二元操作符
     41         {
     42             int left;
     43             int right;
     44         };
     45         struct//对应假名
     46         {
     47             int origin_number;
     48             int hash_table_number;
     49         };
     50         struct//对应字符集
     51         {
     52             char begin;
     53             char end;
     54         };
     55         char value;//对应字符值
     56         int sub;//对应闭包运算符和括号运算符以及问号运算符及+运算符
     57     };
     58 }reg_pattern;//差不多当作抽象语法树吧 
     59 reg_pattern reg_pattern_table[100];
     60 //当前的hash只使用加法hash加上一个模97,因为考虑到97是质数
     61 typedef struct _hash_table//hash表类型 
     62 {
     63     enum _state
     64     {
     65         empty=0,
     66         in_use,
     67         deleted
     68     }state;
     69     char* name_of_alias;
     70     int reg_pattern_number;
     71 }hash_table;
     72 hash_table simple_hash_table[100];
     73 int look_up_hash_table(char* source)//查询一个名字是否在hash表中
     74 {
     75     int string_length;
     76     int counter;
     77     int index;
     78     int result;
     79     string_length=strlen(source);
     80     result=0;
     81     for(index=0;index<string_length;index++)
     82     {
     83         result+=*(source+index);
     84     }
     85     result=result%97;
     86     counter=0;
     87     while(counter<97)//顶多查询97次,如果查询达到了97次,则说明当前hash表中不存在这个字符串
     88     {
     89         if(simple_hash_table[result].state==empty)//如果当前位置为空,说明不存在这个节点,直接返回-1
     90         {
     91             return -1;
     92         }
     93         else
     94         {
     95             if(simple_hash_table[result].state==deleted)//如果为删除状态,则继续向下寻找,注意索引溢出的处理
     96             {
     97                 result=(result+1)%97;
     98                 counter++;
     99             }
    100             else
    101             {
    102                 if(strcmp(source,simple_hash_table[result].name_of_alias)==0)//如果出于使用状态,则开始比较
    103                 {
    104                     return result;//名字相同则返回索引
    105                 }
    106                 else//不同则继续向下寻找
    107                 {
    108                     result+=(result+1)%97;
    109                     counter++;
    110                 }
    111             }
    112         }
    113     }
    114     return -1;//如果找了达到了97次,则返回-1
    115 }
    116 
    117 
    118 
    119 
    120 int insert_hash_table(char* source,int index_of_reg_pattern)
    121 {
    122     int string_length;
    123     int counter;
    124     int index;
    125     int result;
    126     string_length=strlen(source);
    127     result=0;
    128     for(index=0;index<string_length;index++)
    129     {
    130         result+=*(source+index);
    131     }
    132     result=result%97;
    133     counter=0;
    134     while(counter<97)
    135     {
    136         if(simple_hash_table[result].state==in_use)//如果在使用中,则继续下次寻找 
    137         {
    138             result=(result+1)%97;
    139             counter++;
    140         }
    141         else//如果可用,则使用 
    142         {
    143             simple_hash_table[result].state=in_use;
    144             simple_hash_table[result].name_of_alias=source;
    145             simple_hash_table[result].reg_pattern_number=index_of_reg_pattern;
    146             return result;
    147         }
    148     }
    149     return -1;//已经满了 ,插入失败 
    150 }
    151 
    152 int is_begin_of_token()
    153 //判断输入字符是否可以当作一个token的开始符号,这样是为了处理非显示的连接符
    154 //由于转义字符也是可以当作token的开始字符,所以也是返回1
    155 {
    156     switch(reg_input[input_pointer])
    157     {
    158     case '*':
    159     case '?':
    160     case '+':
    161     case '|':
    162     case '.':
    163     case ']':
    164     case ')':
    165     case '':
    166          return 0;
    167     default: 
    168         return 1;
    169     }
    170     //这里两个闭括号的存在都是为了处理非显示的连接符
    171     //而把开括号移动到外部的switch之中
    172     //主要是为了防止括号嵌套引起的错误
    173 }
    174 tackle_invisible_cat()//如果后面跟的是开括号(包括普通括号和假名括号)或者字符,则这时候要去处理隐式连接符
    175 {
    176     if(is_begin_of_token())
    177     {
    178         tackle_cat();
    179     }
    180 
    181 }
    182     
    183 void tackle_or()//处理并操作符,这个处理完之后,输入指针也会后移
    184 {
    185     if(reg_operator_pointer!=0)//如果操作符栈中已经有操作符了
    186     {
    187         if(reg_operator[reg_operator_pointer-1]!='(')//括号另外说
    188         {
    189             name_number++;
    190             if(reg_operator[reg_operator_pointer-1]=='.')
    191                 //如果前面的优先级比当前的高,则处理前面的优先级
    192             {
    193                 printf("name%d is concat of name%d and name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);
    194             }
    195             else
    196                 //这里处理的是相同优先级的情况,其实这里可以与前面的合并的,只不过打印信息不同
    197             {
    198                 printf("name%d is  name%d or name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);
    199             }
    200             token[token_pointer-2]=name_number;
    201             token_pointer--;
    202             reg_operator[reg_operator_pointer-1]='|';
    203             input_pointer++;
    204         }
    205         else//对于括号,则直接入栈
    206         {
    207             reg_operator[reg_operator_pointer++]='|';
    208             input_pointer++;
    209         }
    210     }
    211     else//对于空操作符栈,也是直接入栈
    212     {
    213         reg_operator[reg_operator_pointer++]='|';
    214         input_pointer++;
    215     }
    216 }
    217 int tackle_cat()//处理连接符,处理完之后输入指针不会移动,因为指针本来就指向了后面的字符
    218 {
    219     if(reg_operator_pointer!=0)//如果操作符栈不为空
    220     {
    221         if(reg_operator[reg_operator_pointer-1]=='(')//如果前面有括号,则直接入栈
    222         {
    223             reg_operator[reg_operator_pointer++]='.';
    224         }
    225         else//对于前面不是括号的情况下
    226         {
    227             if(reg_operator[reg_operator_pointer-1]=='.')//优先级相同则输出前面的那个
    228             {
    229                 name_number++;
    230                 printf("name%d is the concat of name%d and name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);
    231                 reg_pattern_table[name_number].type=cat;
    232                 reg_pattern_table[name_number].left=token[token_pointer-2];
    233                 reg_pattern_table[name_number].right=token[token_pointer-1];
    234                 token[token_pointer-1]=0;
    235                 token[token_pointer-2]=name_number;
    236                 token_pointer--;
    237             }
    238             else//否则的话,前面的优先级比当前优先级低,操作符入栈
    239             {
    240                 reg_operator[reg_operator_pointer++]='.';
    241             }
    242         }
    243     }
    244     else//如果操作符栈为空,则入栈
    245     {
    246         reg_operator[reg_operator_pointer++]='.';
    247     }
    248 }
    249 void tackle_parenthesis(void)//处理闭括号模式,这里有点复杂,这里匹配完之后,指针会向后移一位
    250 {
    251     if(reg_operator[reg_operator_pointer-1]=='(')//如果前面那个操作符为开括号,则匹配输出,并把输入指针向后移
    252     {
    253         name_number++;
    254         printf("name%d is (name%d)
    ",name_number,token[token_pointer-1]);
    255         reg_pattern_table[name_number].type=parenthesis;
    256         reg_pattern_table[name_number].sub=token[token_pointer-1];
    257         token[token_pointer-1]=name_number;
    258         input_pointer++;
    259         reg_operator[--reg_operator_pointer]='';
    260         //这时候需要考虑后面的是否少了显示的连接符,如果判断缺少连接符,则需要加上去
    261         tackle_invisible_cat();
    262     }
    263     else//如果闭括号前面还有运算符,那么根据他们的优先级输出,这个时候输入指针是不变的,注意
    264     {
    265         name_number++;
    266         if(reg_operator[reg_operator_pointer-1]=='.')
    267         {
    268             printf("name%d is the concat of name%d and name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);
    269             reg_pattern_table[name_number].type=cat;
    270             reg_pattern_table[name_number].left=token[token_pointer-2];
    271             reg_pattern_table[name_number].right=token[token_pointer-1];
    272         }
    273         else
    274         {
    275             printf("name%d is  name%d or name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);
    276             reg_pattern_table[name_number].type=or;
    277             reg_pattern_table[name_number].left=token[token_pointer-2];
    278             reg_pattern_table[name_number].right=token[token_pointer-1];
    279         }
    280         token[token_pointer-1]=0;
    281         token[token_pointer-2]=name_number;
    282         token_pointer--;
    283         reg_operator_pointer--;
    284     }
    285 }
    286 int tackle_alias(void)//注意这里处理完中括号匹配之后,还需要处理可能存在的非显示连接符
    287 {
    288     int length,index;
    289     char* new_alias;
    290     index=length=0;
    291     while(reg_input[input_pointer+length]!=']')
    292     {
    293         length++;
    294     }
    295     new_alias=malloc(sizeof(char)*(length));//总的长度为length-1,另外加上一个。
    296     for(index=0;index<length-1;index++)
    297     {
    298         new_alias[index]=reg_input[input_pointer+index+1];
    299     }
    300     new_alias[index]='';
    301     input_pointer+=length+1;
    302     index=look_up_hash_table(new_alias);//寻找hash表
    303     if(index!=-1)
    304     {
    305         length=simple_hash_table[index].reg_pattern_number;//找到别名所代表的正则模式
    306         token[token_pointer++]=length;//别名的正则模式的名字入栈
    307         printf("match a alias,%s is the alias of name%d
    ",new_alias,length);
    308         tackle_invisible_cat();
    309         return 1;
    310     }
    311     else//否则,返回错误信息
    312     {
    313         printf("the alias can't be matched
    ");
    314         return -1;
    315     }
    316 }
    317 
    318 int tackle_inter_reg(void)
    319 {
    320     token_pointer=0;
    321     while(reg_input[input_pointer]!='')//还没处理到输入的末尾
    322     {
    323         switch(reg_input[input_pointer])
    324         {
    325         case '\'://注意这里因为在c语言中这个字符也是转义字符,所以要双斜杠才能写出来
    326             input_pointer++;
    327             name_number++;
    328             printf("transition literal is encounted
    ");
    329             printf("the literal is %c
    ",reg_input[input_pointer]);
    330             printf("the literal's token name is name%d
    ",name_number);
    331             token[token_pointer++]=name_number;
    332             reg_pattern_table[name_number].type=literal_char;
    333             reg_pattern_table[name_number].value=reg_input[input_pointer];
    334             input_pointer++;
    335             tackle_invisible_cat();
    336             break;
    337         case '('://对于括号,直接入栈
    338             reg_operator[reg_operator_pointer++]='(';
    339             input_pointer++;
    340             break;
    341         case ')':
    342             tackle_parenthesis();
    343             break;
    344         case '*'://由于这个运算符优先级很高,不需要入栈了,直接拿顶上的token输出就行
    345             name_number++;
    346             printf("name%d is multiple of name%d
    ",name_number,token[token_pointer-1]);
    347             reg_pattern_table[name_number].type=closure;
    348             reg_pattern_table[name_number].sub=token[token_pointer-1];
    349             token[token_pointer-1]=name_number;
    350             input_pointer++;
    351             tackle_invisible_cat();
    352             break;
    353         case '+'://同上
    354             name_number++;
    355             printf("name%d is one or more instance of name%d
    ",name_number,token[token_pointer-1]);
    356             reg_pattern_table[name_number].type=one_or_more;
    357             reg_pattern_table[name_number].sub=token[token_pointer-1];
    358             token[token_pointer-1]=name_number;
    359             input_pointer++;
    360             tackle_invisible_cat();
    361             break;
    362         case '?'://同上
    363             name_number++;
    364             printf("name%d is one or zero instance of name%d
    ",name_number,token[token_pointer-1]);
    365             reg_pattern_table[name_number].type=maybe_exist;
    366             reg_pattern_table[name_number].sub=token[token_pointer-1];
    367             token[token_pointer-1]=name_number;
    368             input_pointer++;
    369             tackle_invisible_cat();
    370             break;
    371         case '['://对于开的中括号,因为中括号中只能允许假名存在,所以直接调用函数处理,
    372                 //处理完之后指针位于闭中括号的后面一个字符,所以switch中不需要考虑闭中括号了
    373                 //注意处理时后面可能接上一个非显示的连接符
    374                 //这个时候需要特殊处理
    375             tackle_alias();
    376             break;
    377         case '|'://对于这个符号,
    378             tackle_or();
    379             break;
    380         default: 
    381             name_number++;
    382             printf("name%d is %c
    ",name_number,reg_input[input_pointer]);
    383             token[token_pointer++]=name_number;
    384             reg_pattern_table[name_number].type=literal_char;
    385             reg_pattern_table[name_number].value=reg_input[input_pointer];
    386             input_pointer++;
    387             tackle_invisible_cat();
    388             break;
    389         }
    390     }
    391     //处理完了输入,这个时候可能还有剩余的内容在栈中
    392     while(reg_operator_pointer>=1)//如果全部的输入都弄完了,可是 操作符栈中还有数据,则输出 
    393     {
    394         name_number++;
    395         if(reg_operator[reg_operator_pointer-1]=='.')
    396         {
    397             printf("name%d is concat of name%d and name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);    
    398             reg_pattern_table[name_number].type=cat;
    399             reg_pattern_table[name_number].left=token[token_pointer-2];
    400             reg_pattern_table[name_number].right=token[token_pointer-1];
    401 
    402         }
    403         else
    404         {
    405             printf("name%d is name%d or name%d
    ",name_number,token[token_pointer-2],token[token_pointer-1]);
    406             reg_pattern_table[name_number].type=or;
    407             reg_pattern_table[name_number].left=token[token_pointer-2];
    408             reg_pattern_table[name_number].right=token[token_pointer-1];
    409 
    410         }
    411         token[token_pointer-2]=name_number;
    412         token_pointer--;
    413         reg_operator_pointer--;
    414     }
    415     return name_number;//返回最后一个生成的token,作为假名的正则代号
    416 }
    417 int tackle_particle()//这里处理一个正则表达式名字的定义,每次处理完之后复用输入数组
    418 {
    419     //首先处理假名部分
    420     int index;
    421     char* name;
    422     input_pointer=0;
    423     while(reg_input[input_pointer++]!=':')//注意这里在停止迭代之后,指针指的是冒号后面的那个字符,
    424     {
    425         //一直向后找,直到找到冒号分隔符
    426     }
    427     //字符串长度比指针的值小1
    428     name=malloc(sizeof(char)*(input_pointer));//考虑到字符串的结尾,于是不需要减1了
    429     for(index=0;index<input_pointer-1;index++)//把名字复制过去
    430     {
    431         name[index]=reg_input[index];
    432     }
    433     name[index]='';
    434     if(reg_input[input_pointer+1]=='-')//如果后面接的是字符集模式,则直接处理,因为这个比较简单
    435     {
    436         name_number++;//新建一个字符集的名字
    437         printf("new set of literal is created
    ");
    438         printf("the set's name is name%d
    ",name_number);
    439         printf("the set begins from %c, and end with %c
    ",reg_input[input_pointer],reg_input[input_pointer+2]);
    440         index=name_number;
    441     }
    442     else
    443     {
    444         index=tackle_inter_reg();//处理当前的小正则表达式,并得到这个表达式所生成的名字
    445     }
    446     insert_hash_table(name,index);
    447     printf("creat a alias: %s is name%d
    ",name,index);//输出,建立了一个新的假名
    448 }
    449 
    450 int main(void)
    451 {
    452     int index;
    453     printf("please type in your short regex definition
    ");
    454     printf("if there is no more definition, just enter a newline
    ");
    455     scanf("%s",reg_input);
    456     while(strlen(reg_input)>=3)//当输入的字符数小于三个的时候,认为结束
    457     {
    458         tackle_particle();
    459         printf("please type in your short regex definition
    ");
    460         printf("if there is no more definition, just enter a newline
    ");
    461         scanf("%s",reg_input);
    462     }
    463     for(index=0;index<97;index++)//这里将所有的假名输出,并释放空间,其实无所谓,反正系统会回收
    464     {
    465         if(simple_hash_table[index].state==in_use)
    466         {
    467             printf("there is a alias whose name is %s
    ",simple_hash_table[index].name_of_alias);
    468             free(simple_hash_table[index].name_of_alias);
    469         }
    470     }
    471 }
  • 相关阅读:
    Linux的五个查找命令
    Linux中ftp不能上传文件/目录的解决办法
    linux下的chmod,chown和chgrp
    Memcache基础教程
    Web服务器磁盘满故障
    linux定时任务生产java服务无法执行问题案例
    Linux 用户和用户组管理
    10-xargs 简明笔记
    51-du 显示关于目录层次结构或文件磁盘使用情况的信息
    46-df 显示磁盘空间的使用情况
  • 原文地址:https://www.cnblogs.com/huangfeidian/p/3151064.html
Copyright © 2020-2023  润新知