最初步的正则表达式引擎:支持子表达式替换
2013-06-22 20:16
246 查看
子表达式的语法为
name:regex
在处理时,会把name加入进符号表中,方法是hash,最简单的加法
而在regex中如果碰到[name]这种模式,则尝试去找符号表,如果找不到则报错。
代码如下,欢迎大家测试。
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 '\0': 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\n",name_number,token[token_pointer-2],token[token_pointer-1]); } else //这里处理的是相同优先级的情况,其实这里可以与前面的合并的,只不过打印信息不同 { printf("name%d is name%d or name%d\n",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\n",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)\n",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]='\0'; //这时候需要考虑后面的是否少了显示的连接符,如果判断缺少连接符,则需要加上去 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\n",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\n",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,另外加上一个\0。 for(index=0;index<length-1;index++) { new_alias[index]=reg_input[input_pointer+index+1]; } new_alias[index]='\0'; 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\n",new_alias,length); tackle_invisible_cat(); return 1; } else//否则,返回错误信息 { printf("the alias can't be matched\n"); return -1; } } int tackle_inter_reg(void) { token_pointer=0; while(reg_input[input_pointer]!='\0')//还没处理到输入的末尾 { 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\n",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\n",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\n",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\n",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));//考虑到字符串的结尾\0,于是不需要减1了 for(index=0;index<input_pointer-1;index++)//把名字复制过去 { name[index]=reg_input[index]; } name[index]='\0'; index=tackle_inter_reg();//处理当前的小正则表达式,并得到这个表达式所生成的名字 insert_hash_table(name,index); printf("creat a alias: %s is name%d\n",name,index);//输出,建立了一个新的假名 } int main(void) { int index; printf("please type in your short regex definition\n"); printf("if there is no more definition, just enter a newline\n"); while(scanf("%s",reg_input)) { tackle_particle(); printf("please type in your short regex definition\n"); printf("if there is no more definition, just enter a newline\n"); } for(index=0;index<97;index++) { if(simple_hash_table[index].state==in_use) { printf("there is a alias whose name is %s\n",simple_hash_table[index].name_of_alias); free(simple_hash_table[index].name_of_alias); } } }
相关文章推荐
- 面试,为什么总问些没用的,及emacs支持正则表达式替换
- 最初步的正则表达式引擎:增加了字符集表示和?运算符和+运算符
- 最初步的正则表达式引擎:生成nfa
- 发布一个自己写的文件重命名工具,支持正则表达式替换和自定义规则。
- Linux C 支持正则表达式的字符串替换函数
- 最初步的正则表达式引擎
- 最初步的正则表达式引擎:将显示的连接符改为了非显示的连接符
- 最初步的正则表达式引擎:nfa的转换规则。
- 最初步的正则表达式引擎:在上个版本的基础上增加了转义字符
- java String类对正则表达式的支持
- [置顶] 【Scala 正则表达式】Regex 类来支持正则表达式
- Django查找网站项目根目录和对正则表达式的支持
- 正则表达式匹配和替换
- Word中使用正则表达式进行查找和替换
- SQL Server中利用正则表达式替换字符串
- 详解Ruby中正则表达式对字符串的匹配和替换操作
- JAVA正则表达式匹配,替换,查找,切割
- [Regular] 3、正则表达式引擎的构造
- SQL Server中利用正则表达式替换字符串
- EditPlus正则表达式替换字符串详解