正则表达式
http://www.regexlab.com/zh/regref.htm
正则表达式(regular expression)就是用一个“字符串”来描述一个特征,然后去验证另一个“字符串”是否符合这个特征。比如 表达式“ab+” 描述的特征是“一个 'a' 和 任意个 'b' ”,那么 'ab', 'abb', 'abbbbbbbbbb' 都符合这个特征。
正则表达式可以用来:(1)验证字符串是否符合指定特征,比如验证是否是合法的邮件地址。(2)用来查找字符串,从一个长的文本中查找符合指定特征的字符串,比查找固定字符串更加灵活方便。(3)用来替换,比普通的替换更强大。
正则表达式一般有如下基本规则, 本文尝试编码实现:
1. * 表示任意个 2. +表示至少一个 3. ?表示0到一个 4. {n}表示精确匹配n个 5. []表示字符集合,匹配所包含的任意一个字符 6. x|y表示匹配 x 或 y
--------
实现思路分析
---- 分析 ----
1、 首先正则表达式匹配, 是使用 正则字符串 匹配 普通字符串,
第一件要做的是,普通字符串解析,所以普通字符串解析功能需要实现。
2、 其次, 正则字符串也需要解析, 需要实现 正则字符串(表达式) 解析功能。
3、 解析正则字符串过程, 及时进行 与普通字符串的匹配工作。
每当解析出一个正则字符,则执行此正则字符, 与普通字符的匹配。
---- 待实现的正则表达元素 ------
分为三类:
(1) 修饰符, 用于修饰字符出现的次数:
1. * 表示任意个 2. +表示至少一个 3. ?表示0到一个 4. {n}表示精确匹配n个
(2) 可匹配多种字符的正则字符写法
5. []表示字符集合,匹配所包含的任意一个字符
(3) 可匹配多种字符的正则字符写法
6. x|y表示匹配 x 或 y
--- 实现步骤 ---
1、 实现第 1 点 字符串解析,将字符串作为对象定义, 实现其应有的方法。
2、 先不考虑正则字符串的解析, 将正则字符串作为普通字符串看待,
实现正则字符串解析 与 普通字符串的匹配工作。
此步骤实现了, 程序的基本流程。
3、 将正则字符串的解析添加到第二步的流程中,
(1) * + ?表示对 字符出现数目的修饰, 属于修饰符, 在解析正则表达式, 只需要读取一个字符匹配,
, 且匹配普通字符串, 可以统一到{开始次数,结束次数}中。 对于三个字符, 实现逻辑大体相同, 可以第一步实现。
(2) {n} 在正则字符后匹配一个三个符,需要在兼容* + ?解析后,
支持多个字符读取, 并匹配{n},解析出n值。 在(1)上做结构性修改即可, 所以安排第二步实现。
(3) [] 是对正则字符的主体进行的扩展, 即其支持的匹配字符可以是多个,
此步骤实现后, 对正则字符是个扩充, (1)(2)步骤, 只需要char, 此步骤需要string。
(4) | 可匹配多种字符的正则字符写法, 同(3), 解析过程将 可选择的字符 存储到 (3)中实现的string数据结构中。
------- 实现模式 ----------
1、 修饰符的提取: 每种修饰符的 提取方法, 和 修饰符对普通字符串的匹配方法, 各不相同,
但是 正则字符的提取 然后 执行匹配的 流程是 固定的, 将此过程写成固定的方法,
将修饰符的 两种函数 做成一个表, 流程中一次调用 提取函数, 提取成功, 则执行响应修饰符的 普通匹配函数。
/* ------------------------------ 全局变量 --------------------------------- */ T_PATCHAR_MODIFIER g_patCharModifier[] = { {MODIFIER_TYPE_FIXCOUNT, MatchFixCount, DoMatchStrByFixCount}, {MODIFIER_TYPE_QUESTIONMARK, MatchQuestionMark, DoMatchStrByQuestionMark}, {MODIFIER_TYPE_ZEROPLUS, MatchZeroPlus, DoMatchStrByZeroPlus}, {MODIFIER_TYPE_ONEPLUS, MatchOnePlus, DoMatchStrByOnePlus}, // plain 必须放在最后, 以满足, 其他修饰符都未匹配,默认使用此匹配符号 {MODIFIER_TYPE_NOMODIFIER, MatchNoModifier, DoMatchStrByNoModifier}, };
2、 正则字符提取, [] 和 | 都是要 提取 可匹配的字符 集合, 也采用修饰符的实现模式,
将 [] 和 | 的解析函数, 和 普通字符解析函数 做成一个表,
对于每次字符匹配, 匹配成功, 则确定了 可匹配字符集。
GetOptCharsFunc g_getOptCharsFunc[] = { GetOptCharsByOrExpress, // 0|1 GetOptCharsBySetExpress, // [01] // no express must be set to tail, as default valid action GetOptCharsByNoExpress, // 0 };
C 代码实现
https://github.com/fanqingsong/code-snippet/blob/master/C/regularExpress/cRegFunc.cpp
#include <stdio.h> #include <string.h> #include <assert.h> #include <malloc.h> #include <stdarg.h> /* ------------------------------ 声明区域 --------------------------------- */ // list node typedef struct node{ char* str; struct node* next; } T_NODE, *PT_NODE; typedef enum returnType { FAIL = 0, SUCCESS, } E_RETURN_TYPE; typedef enum boolType { FALSE = 0, TRUE, } E_BOOL_TYPE; // pattern modifier : {} * + typedef enum ModifierType{ MODIFIER_TYPE_NOMODIFIER, // PLAIN CHAR MODIFIER_TYPE_FIXCOUNT, // {X} MODIFIER_TYPE_ZEROPLUS, // * MODIFIER_TYPE_ONEPLUS, // + MODIFIER_TYPE_QUESTIONMARK, // ? } E_MODIFIER_TYPE; typedef struct patternChar{ char szOptChars[128]; // option char , for muliti char from 0|2 and [23] express int iOptCharsLen; E_MODIFIER_TYPE modifyType; // modifier type : * + ? int modifyPara; } T_PAT_CHAR, *PT_PAT_CHAR; typedef struct stringObj{ char* szStr; int iLen; int iIndex; } T_STRING_OBJ, *PT_STRING_OBJ; typedef E_BOOL_TYPE (*MatchModifier)(PT_STRING_OBJ, PT_PAT_CHAR); typedef E_BOOL_TYPE (*MatchStrByModifier)(PT_PAT_CHAR, PT_STRING_OBJ); typedef struct PatCharModifier{ E_MODIFIER_TYPE modifyType; MatchModifier matchModifier; MatchStrByModifier matchStrByModifier; } T_PATCHAR_MODIFIER, PT_PATCHAR_MODIFIER; typedef E_BOOL_TYPE (*GetOptCharsFunc)(PT_PAT_CHAR ptPatChar, PT_STRING_OBJ ptPatObj); void MyPrintf(char* fmt, ...); char* DupSubStr(char* str, int start, int end); void StopWithMsg(char* msg); /* 修饰符获取 和 依照修饰符匹配函数簇 */ E_BOOL_TYPE MatchFixCount(PT_STRING_OBJ ptPatObj, PT_PAT_CHAR ptPatChar); E_BOOL_TYPE DoMatchStrByFixCount(PT_PAT_CHAR ptPatChar, PT_STRING_OBJ ptStrObj); E_BOOL_TYPE MatchQuestionMark(PT_STRING_OBJ ptPatObj, PT_PAT_CHAR ptPatChar); E_BOOL_TYPE DoMatchStrByQuestionMark(PT_PAT_CHAR ptPatChar, PT_STRING_OBJ ptStrObj); E_BOOL_TYPE MatchZeroPlus(PT_STRING_OBJ ptPatObj, PT_PAT_CHAR ptPatChar); E_BOOL_TYPE DoMatchStrByZeroPlus(PT_PAT_CHAR ptPatChar, PT_STRING_OBJ ptStrObj); E_BOOL_TYPE MatchOnePlus(PT_STRING_OBJ ptPatObj, PT_PAT_CHAR ptPatChar); E_BOOL_TYPE DoMatchStrByOnePlus(PT_PAT_CHAR ptPatChar, PT_STRING_OBJ ptStrObj); E_BOOL_TYPE MatchNoModifier(PT_STRING_OBJ ptPatObj, PT_PAT_CHAR ptPatChar); E_BOOL_TYPE DoMatchStrByNoModifier(PT_PAT_CHAR ptPatChar, PT_STRING_OBJ ptStrObj); // 解析组合模式字符 表达形式 0|1 [01] E_BOOL_TYPE GetOptCharsByOrExpress(PT_PAT_CHAR ptPatChar, PT_STRING_OBJ ptPatObj); E_BOOL_TYPE GetOptCharsBySetExpress(PT_PAT_CHAR ptPatChar, PT_STRING_OBJ ptPatObj); E_BOOL_TYPE GetOptCharsByNoExpress(PT_PAT_CHAR ptPatChar, PT_STRING_OBJ ptPatObj); /* ------------------------------ 全局变量 --------------------------------- */ T_PATCHAR_MODIFIER g_patCharModifier[] = { {MODIFIER_TYPE_FIXCOUNT, MatchFixCount, DoMatchStrByFixCount}, {MODIFIER_TYPE_QUESTIONMARK, MatchQuestionMark, DoMatchStrByQuestionMark}, {MODIFIER_TYPE_ZEROPLUS, MatchZeroPlus, DoMatchStrByZeroPlus}, {MODIFIER_TYPE_ONEPLUS, MatchOnePlus, DoMatchStrByOnePlus}, // plain 必须放在最后, 以满足, 其他修饰符都未匹配,默认使用此匹配符号 {MODIFIER_TYPE_NOMODIFIER, MatchNoModifier, DoMatchStrByNoModifier}, }; GetOptCharsFunc g_getOptCharsFunc[] = { GetOptCharsByOrExpress, // 0|1 GetOptCharsBySetExpress, // [01] // no express must be set to tail, as default valid action GetOptCharsByNoExpress, // 0 }; /* ------------------------------ 公共函数 --------------------------------- */ // MyPrintf == puts + format args void MyPrintf(char* fmt, ...) { va_list args; va_start(args, fmt); vprintf(fmt, args); va_end(args); printf(" "); } // 创建新的存储空间, 拷贝字符串的子串 char* DupSubStr(char* str, int start, int end) { char* newStr = NULL; // sub string len 开区间 计算子串长度 int len = end - start + 1; // regarding ending zero len++; newStr = (char*)malloc( sizeof(char)*len ); if ( NULL == newStr ) { return NULL; } newStr[len-1] = '