淺談正規表示式背後的基本原理

這裡好像沒有人發表於2017-08-14

一、寫在前面

搞程式設計的都知道正規表示式是什麼東西,這裡就不多囉嗦了,需要強調的是,這篇文章並不是教你怎麼去使用用正規表示式,正規表示式的語法並不是本文的重點,這篇文章的目的就是剝開正規表示式的語法糖,來看一看正規表示式最本質的原理,如果文章中有錯誤或者紕漏,歡迎批評指正。

二、什麼是語法糖

在上面我提到了語法糖的概念,也許有人還不清楚語法糖是什麼東西,這裡簡單的說一下。

語法糖(Syntactic sugar),也譯為糖衣語法,是由英國電腦科學家彼得·約翰·蘭達(Peter J. Landin)發明的一個術語,指計算機語言中新增的某種語法,這種語法對語言的功能並沒有影響,但是更方便程式設計師使用。通常來說使用語法糖能夠增加程式的可讀性,從而減少程式程式碼出錯的機會。——摘自 百度百科

可以這麼說吧,像C、C++、Java等等這些程式語言都可以看做成語法糖,因為最終還是得將這些高階程式語言翻譯成機器程式碼,也就是10101……這樣的形式,要知道所有的可執行程式最終都可以只需要賦值跳轉兩種結構即可,而高階語言都是對這兩種結構的封裝,來適應不同的應用場景,除了機器程式碼,彙編和這些高階語言都是語法糖,一層一層的包裝,達到簡化開發的目的,大家想想,如果沒有這些語法糖,那我們豈不是得天天用機器程式碼寫程式?

三、算數表示式

這裡先引入一個小小的例子,我想在閱讀這篇文章的人都知道什麼是算數表示式,最基本的算數表示式:

1,2,3,4,5,6,7,8,9,0,……
+,-,*,/,……

這些都是最基本的算數表示式,而由這些最基本的算數表示式可以構造出更加複雜的複合表示式比如1+1,3*5等等,無論是基本的還是複合的,它們都是算數表示式,通過這個例子,來自然的過渡到下面正規表示式的內容,其實本質上算數表示式和正規表示式的道理是差不多的。

四、正規表示式

構成正規表示式最基本的就是給定的字符集∑={c1,c2,c3,……,cn},這就相當於算數表示式中的0,1,2,3……這些基本算數表示式。
接下來呢,就是他的歸納定義,來告訴我們如何通過最基本的字符集構造出複雜的正規表示式:

  1. 空串ε是正規表示式。
  2. 對於任意字元c∈∑,c是正規表示式
  3. 如果M,N是正規表示式,則以下也是正規表示式:

    
    **選擇**  M|N = {M,N}
    **連線**  MN  = {mn | m∈M, n∈N}
    **閉包**  M*  = {ε,M,MM,MMM,……}
    

不難看出,以上的歸納定義給出了正規表示式最基本的的形式,無論多麼複雜的正規表示式都是在這個基礎上構成的。

現在我們通過一個小例子來加深對上面概念的理解:


給定一個字符集∑={a,b},可以寫出那些正規表示式呢?
1.  ε
2.  a,b
3.  ε|ε,ε| a , ε| b ,……
4.  εa , εb , ab , εε , ……
5.  a(ε| a) , b(ε|b),……
6.  ε* , (a(ε| a))*,……
7.  ……

也就是說,單個的字元都是正規表示式,它們按照上面的定義組合起來依然是正規表示式,正規表示式與正規表示式相互組合又可以生成新的正規表示式,在複雜的正規表示式都是由這些基本的正規表示式構成,當然了上面這些只是該字符集的正規表示式的一小部分,因為這個字符集的正規表示式集合是一個無限集,到這裡,我想大家應該有所體會。

我們再來看一個例子:


我們用上面的正規表示式的概念來構造出用來描述C語言識別符號的正規表示式:
首先給定字符集,我們都知道C語言的字符集有ASCII碼構成。
C語言識別符號的格式:以字母或下劃線開頭,後面跟零個或多個字母、數字或下劃線。

該怎麼用正規表示式來描述呢?

(a|b|c|……|z|A|B|C|……|Z|_)(a|b|c|……|z|A|B|C|……|Z|0|1|2|3|……|9|_))*

首先來看,這個正規表示式是由兩個子表示式連線而成,每個子表示式都是用選擇符|構成,又因為第二個子表示式可以出現零或多次,所以加上閉包,是不是看的腦袋都大了,是不是覺得平時什麼時候這麼寫過正規表示式,下面就得說說語法糖的作用啦。

五、正規表示式中的語法糖

大家接觸到的正規表示式的語法似乎是有差異的,比如POSIX風格正規表示式和Perl風格正規表示式,要知道無論什麼風格的正規表示式它們背後的原理都是一樣的,只是在上層提供的語法糖不一樣而已,實際應用的過程中都是根據上面的原理演變過來的,語法糖可以大大簡化正規表示式的形式,變得更容易閱讀和理解,就像下面的對應關係一樣。

[c1-cn] == c1|c2|c3|……|cn

e? == ε|e

e+ == (e*)ε

e{i,j} == i到j個e連線

這裡就不一一舉例了,無論上面的對應關係中左邊的語法如何變化,它所對應的右邊的基本原理都是一樣的。

六、小結

正規表示式可以寫的非常複雜,複雜到除了作者外很少有人看的懂的,曾經我也是一度不能自拔,但隨著學習的深入,慢慢的發現,剝開正規表示式表面的東西,去看背後的原理,才有一種恍然大悟的感覺。

相關文章