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