手撕正则表达式
我们先撕简单的。a ab a|b aa* a(a|b)* 先不管匹配任意字符的. 重复>=1次的+ [^0-9]除0-9外 digit数字等。

正则表达式(regular expression, re)为啥叫表达式,不叫正则字符串之类?因为它是个表达式。3+5*2是个表达式;两个字符串可以有连接运算,如"a"+"b"或"a"."b"得到"ab"。
在正则表达式里,a,b,c就像2,3,5,是被运算的数,. | * ()是运算符。请注意:ab是a和b拼接,人们为了省事不把拼接运算符写出来。
(3+5)*2=16,3+(5*2)=13。如果没有四则运算优先级和括号,3+5*2等于16还是13?运算符后置(后缀表达式)没有歧义,例如35+2*是mul(add(3,5), 2),352+*是mul(3, add(5,2))。mul: multipy. What are infix, postfix and prefix expressions?
我们分3步走:
- 把a(a|b)*变成aab|*.这样的后缀表达式,40行程序。ab是a和b拼接,是a.b的缩写(中间有个.)
- 用Thompson算法把后缀表达式变成NFA,号称4行 (case, case, case, default)
- 用NFA检查是否匹配,号称10行
第1步中缀变后缀请看代码。
第2步后缀变NFA。NFA可以像积木一样拼起来。下面分别是a, ab, a|b, a*的NFA:
图片是用dot - graphviz version 2.49.0画的。如 dot -o ab.png -Tpng todot.txt 或 dot -Tpng todot.txt >ab.png 。dot -h看帮助。
https://files.cnblogs.com/files/blogs/714801/Graphviz.7z 1996KB 可能是最小的了,带grep.exe
拼接NFA的代码:
NFA postfix_to_nfa(const char* pfstr) { Stackstk; for (const char* p = pfstr; *p; p++) { switch (*p) { case '.': stk.push(stk.pop() + stk.pop()); break; case '|': stk.push(stk.pop() | stk.pop()); break; case '*': stk.push(*stk.pop()); break; default: stk.push(*p); } } NFA nfa = stk.pop(); if (!stk.empty()) error; return nfa; }
运算符函数也不长,含打印,匹配等全部代码180行:


// 从ChrisZZ(zchrissirhcz@gmail.com)的程序改来的 #include#include string.h> #include string> #include using namespace std; #define error throw __LINE__ templateclass T>struct Stack : public stack { T pop() { T t = top(); stack ::pop(); return t; } }; const char END = '