正則表示式的學習之路(為學習 自動機 )
阿新 • • 發佈:2019-01-28
一開始想要學習正則表示式是想學習自動機演算法,後來看自動機演算法是正則表示式的引擎,就決定先學一下正則表示式
以下資源取自很多網上資源,包括但不限於 百度百科 , CSDN,部落格園的一些部落格,我還包括一些國外文章的翻譯,在此就不一一給出連線了,如有侵權,請及時聯絡我,我會盡量按照被侵權方要求解決問題
(辣雞需要看其他人的資料才能學習)QAQ。。。
/*---------------------自動機--------------------------*/ DFA和NFA自動機是正則表示式的引擎,又叫正則引擎。 因此,我覺得如果想學自動機的一些演算法的話,瞭解正則表示式是必須的。 現在先了解一下正則表示式,畢竟磨刀不誤砍柴工,下面是一些東拼西湊的概念: 正則表示式 又稱 規則表示式,(Regular Expression),通常用來檢索,替換那些符合某個模式(規則)的文字 //------------概念: 正則表示式是對字串操作的一種邏輯公式,就是事先定義好的一些特定字元,以及這些特殊字元的組合 組成一個 規則 字串 ,這個 規則字串 用來表達對字串的一種過濾邏輯。 //------------簡介: 正則表示式是對字串(普通字元)和特殊字元,操作的一種邏輯公式, 用實現定義好的一些特定字元以及這些特定字元的組合,組成一個 規則字串 這個規則字串 用來表達對字串的一種過濾邏輯 。 正則表示式是一種文字模式,模式描述 在搜尋文字時要匹配的一個或多個字串 //------------目的: 給定一個正則表示式個另一個字串,我們可以達到兩個目的: 1. 給定字串是符合正則表示式的過濾邏輯(匹配)。 2. 可以通過正則表示式,從字串中獲取我們想要的特定部分。 //------------符號: 匹配樣例:對於字串"testing",可以匹配到"testing"和"testing123",但是匹配不到"Testing"。 \ :轉義字元 ^ :匹配輸入字行首。 $ :匹配輸入行尾。 * :匹配前面的子表示式任意次。*等價於{0,}。 + :匹配前面的子表示式一次或多次(大於等於1次)。+等價於{1,}。 ? :匹配前面的子表示式零次或一次。?等價於{0,1}。 {n} :n是一個非負整數。匹配確定的n次。 例:"o{2}"不能匹配"Bob"中的"o",但是能匹配"food"中的兩個"o"。 {n,}:n是一個非負整數。至少匹配n次。 例:"o{2,}"不能匹配"Bob"中的"o",但能匹配"foooood"中的所有"o"。 {n,m}:m和n均為非負整數,其中n<=m。最少匹配n次且最多匹配m次。 例:"o{1,3}"將匹配"fooooood"中的前三個"o"為一組,後三個"o"為一組。 注意!!:在逗號和兩個數之間不能有空格。 ? :當該字元緊跟在任何一個其他限制符('*','+','?',"{n}","{n,}","{n,m}")後面時,匹配模式是非貪婪的。 對於字串"oooo": 1.非貪婪模式儘可能少地匹配所搜尋的字串。例: "o+"將盡可能多地匹配"o",得到結果["oooo"] 2.而預設的貪婪模式則儘可能多地匹配所搜尋的字串。例: "o+?"將盡可能少地匹配"o",得到結果 ['o', 'o', 'o', 'o'] . :匹配除"\n"和"\r"之外的任何單個字元。 a|b :匹配x或y。 例如,"z|food"能匹配"z"或"food"(此處請謹慎)。"[zf]ood"則匹配"zood"或"food"。 [abc]:字元集合。匹配所包含的任意一個字元。 例:"[abc]"可以匹配"plain"中的"a"。 [^abc]:負值字元集合。匹配未包含的任意字元。 例:"[^abc]"可以匹配"plain"中的"plin"任一字元。 [a-z]:字元範圍。匹配指定範圍內的任意字元。 例:"[a-z]"可以匹配"a"到"z"範圍內的任意小寫字母字元。 [^a-z]:負值字元範圍。匹配任何不在指定範圍內的任意字元。 例:"[^a-z]"可以匹配任何不在"a"到"z"範圍內的任意字元。 () :將'(' 和 ')' 之間的表示式定義為"組"(group),並且將匹配這個表示式的字元儲存到一個臨時區域(一個正則表示式中最多可以儲存9個),它們可以用 "\1" 到"\9" 的符號來引用。 | 將兩個匹配條件進行邏輯 或 。 例:正則表示式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:這個元字元不是所有的軟體都支援的。 //------------引擎: 正則引擎可以分為兩大類,DFA,NFA DFA引擎: 線上執行,不要求回溯(永遠不測試相同字元兩次)。DFA引擎確保匹配最長可能的字串; DFA引擎只包含有限狀態,因此不能匹配具有反響引用的模式; 並且因為他不構造顯示擴充套件,所以不能捕獲子表示式。 NFA引擎: 執行 貪婪的 回溯演算法, 以 指定順序 測試正則表示式的所有可能的擴充套件並接受 第一個匹配項 傳統的NFA構造正則表示式的特定擴充套件以獲得成功的匹配,所以他可以捕獲子表示式匹配和匹配的反向引用 傳統的NFA回溯可以訪問完全相同的狀態多次(如果通過不同的路徑到達該狀態) 因此,在最壞的情況下,它的執行速度可能非常慢。 傳統的NFA接受它找到的第一個匹配,所以它還可能會導致其他(可能更長)匹配未被發現。 POSIX NFA引擎:(現在不想學,就不看了,以後再補上吧) 這裡的自動機特指有限狀態自動機,簡稱FA 根據狀態轉移的性質又分為確定的自動機 DFA 和非確定的自動機 NFA 。 FA的表達能力等價於正規表示式或者正規文法 。 FA可以看做是一個 有向帶權圖 。 圖的 定點集合 成為自動機的 狀態集合, 圖的 權值集合 為自動機的 字母集合 。 圖的 邊 代表了自動機中 狀態變化的情況。 此外,根據需要,自動機還需指定 初始狀態 和終態。 FA最基本的左右就是形式化描述,而且有利於程式設計實現,以下開始介紹DFA自動機 //-----------------------DFA自動機 ----------- https://blog.csdn.net/u012061345/article/details/52092436?locationNum=11 https://blog.csdn.net/qq_36827957/article/details/74357283 //----- 考慮僅有字元{a,b}組成的字串,要求字串中 字母b 必須成對出現,否則字串非法。 這個規則實現起來非常簡單,不需要自動機也完全可以。但是我們現在考慮是有自動機進行判斷。 該規則的正規表示式描述是:(a|bb)*。*運算代表重複若干次,包括0次。 做一個圖來表示描述該規則的DFA。 令 狀態1 為初始狀態,顯然在 狀態1 上,我們還沒有違反規則。因此,經過字母a 以後我們還可以回到 狀態1 。 經過 字母b 後就不能回到 狀態1 了,此時需要一個新狀態,令其為 狀態2。 狀態2表示 待定的 狀態,在這個狀態時不能肯定字串是非法的,但也不是合法的。 在 狀態2 上,如果經過 字母b ,就回到了合法的狀態(狀態1); 如果經過 字母a ,則該字元肯定是非法的。建立一個 狀態3 ,表示非法狀態。 狀態3 比較簡單,已經到了非法狀態,其後的任何字母都不會改變這個狀態了。 因此,該DFA表示為: 1 3 ^ ^ |a |a,b | | V V 1 2 3 ---b--> ---a---> <--b--- 程式實現: 狀態和字母都被編碼成整數,使用一個矩陣表示狀態轉移,再寫一個函式表示自動機的執行, 對於每個字串,從狀態1開始執行,執行完畢進行狀態判斷即可。 最後能停留在狀態1的字串才是符合規則的,其他的都是非法的。 程式碼: #include<cstdio> int DFA[4][2]={ {0}, //0 0,1 {1,2}, //1 0,1 {3,1}, //2 0,1 {3,3} //3 0,1 }; int START_STATE=1; int run(char const word[]) { int state=START_STATE; for(char const *p=word;*p;++p) { state=DFA[state][*p-'a']; if(state==3) return state; } return state; } int main() { char a[],b[],c[]; printf("%d\n",run(a)); printf("%d\n",run(b)); printf("%d\n",run(c)); return 0; } //--------------HDU-2206-IP的計算 題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=2206 ac程式碼, 錯誤狀態記作0 int const T[17][3]={ // d.other // 0 1 2 /*0*/ 0,0,0, /*1*/ 2,0,0, 3,5,0, 4,5,0, 0,5,0, /*5*/ 6,0,0, 7,9,0, 8,9,0, 0,9,0, /*9*/ 10,0,0, 11,13,0, 12,13,0, 0,13,0, /*13*/ 14,0,0, 15,0,0, 16,0,0, 0,0,0 }; inline int judge(char ch) { if(ch>='0'&&ch<='9')//是個數 return 0; if(ch=='.')//是點 return 1; return 2;//啥都不是 } int run(char const word[]) { int state=1; for(char const *p=word;*p;++p)//遍歷每個字元 { state=T[state][judge(*p)]; if(state==0) return 0; } return state;// } inline bool isfinal(int state) { return state==14||state==15||state==16; } int main() { char s[110]; while(gets(s)) { if(isfinal(run(s))==0)//不符合要求 { cout<<"NO"<<endl; continue; } int a,b,c,d; sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d); if(a>255||b>255||c>255||d>255) cout<<"NO"<<endl; else cout<<"YES"<<endl; } } //----------POJ-3332-Parsing Real Numbers 題目連結:http://poj.org/problem?id=3332 題目大意就是輸入一串字串,然後判斷這個串中是否包含有效的實數(任意書寫方式,包括但不限於科學計數法等) 是則輸出: LEGAL 否則: ILLEGAL 解題思路: 對於一個 字串,它的狀態有以下幾種: 一個符合要求的數字可以拆解成下面的式子: +- d . d Ee +- d _ 1 2 3 4 5 6 7 8 9 然後輸入對應的DFA轉移陣列,下面是AC程式碼: //#pragma comment(linker, "/STACK:1024000000,1024000000") #include<stdio.h> #include<string.h> #include<math.h> #include<stdlib.h> //#include<map> //#include<set> #include<deque> #include<queue> #include<stack> #include<bitset> #include<string> #include<fstream> #include<iostream> #include<algorithm> using namespace std; #define ll long long //#define max(a,b) (a)>(b)?(a):(b) //#define min(a,b) (a)<(b)?(a):(b) #define clean(a,b) memset(a,b,sizeof(a))// 水印 //std::ios::sync_with_stdio(false); const int MAXN=1e5+5; const ll INF=1e18; const ll mod=1e9+7; int DFA[10][6]={ 0,0,0,0,0,0, 3,0,0,2,0,0,//數字,+- 3,0,0,0,0,0,//+-之後只能是數字 3,4,6,0,9,0,//數字迴圈 . Ee 直接空格(結尾) 5,0,0,0,0,0,// . 之後的數字 5,0,6,0,9,0,//數字迴圈 Ee 空格 8,0,0,7,0,0,//Ee之後的 +- 8,0,0,0,0,0,//+-之後的數字 8,0,0,0,9,0,//數字迴圈 空格 0,0,0,0,9,0 // 空格 }; int get_char(char ch) { if(ch<='9'&&ch>='0') return 0; if(ch=='.') return 1; if(ch=='E'||ch=='e') return 2; if(ch=='+'||ch=='-') return 3; if(ch==' ') return 4; return 5; } bool judge(char *s) { int index=1; for(char *p=s;*p;++p) { index=DFA[index][get_char(*p)]; if(!index) return 0; } if(index==3||index==5||index==8||index==9) return 1; else return 0; } int main() { int T; cin>>T; getchar(); while(T--) { char s[1100]; gets(s); char *p=s; while(*p==' ')//找到第一個字元 p++; if(judge(p)) cout<<"LEGAL"<<endl; else cout<<"ILLEGAL"<<endl; } } //------------------ 擴充套件連結: 正則表示式 的整理: https://blog.csdn.net/github_36498175/article/details/63262348 python中利用正則表示式爬取資訊: https://blog.csdn.net/weixin_41580211/article/details/79089038 正則表示式引擎的構建: https://swtch.com/~rsc/regexp/regexp1.html
未完待續。。。
很快就有