(轉)爲何須要正則表達式 by 王珢

爲何須要正則表達式

by 王垠正則表達式

學習Unix最開頭,你們都學過正則表達式(regexp)。但是有沒有人考慮過咱們爲何須要正則表達式?安全

正則表達式原本的初衷是用來從無結構的字符串中提取信息,卻不知這正好是Unix的缺陷所在。Unix用無結構的字符串來表示數據,致使了諸多複雜的基於regexp的軟件的誕生。sed, AWK, Perl, … 都是爲了一樣的目的來到這個世界上的。若是不是由於Unix用字符串來表示數據,咱們就會擁有按數據結構類型的直接存儲,而不須要折騰regexp。正則表達式有它本身的價值(針對天然語言),可是咱們其實不須要把它應用到程序語言和操做系統裏面。數據結構

正則表達式自己用一個字符串來表示,這帶來另一些問題。由於正則表達式的本質不是字符串,而是一個數據結構。學過計算理論的人可能知道這個數據結構叫作NFA(nondeterministic finite automaton,非肯定性有限自動機)。全部的數據結構應該由程序語言自己來表示,就像用Java構造一個對象用 new ClassA("a") 同樣。可是正則表達式強迫你把這個簡單的構造函數調用寫成一個字符串。因此在這個比方之下,你得寫成new ClassA(\"a\")。這樣當你想要組合這些表達式的時候就發現,正則表達式幾乎都是不可組合(compose)的。你幾乎不可能不能把兩個regexp的變量A和B安全拼接成一個,好比用Java的字符串拼接A+B。由於你不知道這兩個字符串拼在一塊兒以後,那些稀奇古怪的符號會出現什麼交叉反應,使得最後的識別的東西根本不是你想要的。函數

在正則表達式中,因爲正則表達式自己的構造函數與數據自己合併到一塊兒,咱們不得不對某些「特殊字符」進行escape。這些特殊字符,實際上是用來描述NFA的記號,它們屬於更高一層的語言。但是在正則表達式裏,它們與NFA節點裏的字符混爲一談。好比很簡單的一個block comment的正則表達式,卻要寫成這個樣子:post

/\\*([^\\*]|[^/])*\\*/

顯然這樣的表達式很容易出錯。 若是咱們用程序語言的表達式來構造這個表達式,它應該是這樣:學習

(@... "/*" (@*(@!"*/")) "*/" )

在這個我本身設計的Scheme表達式裏,以@開頭的標識符都是構造函數。其中@...是構造sequence,@* 是構造一個zero-or-more的匹配,@!構造一個否認匹配。這個表達式是說:「以/ *開頭,接着零個或者多個不是* /的字符,最後接着一個* /。這樣一來清晰明瞭,什麼表達式在什麼「層次」都很清楚,不須要什麼反斜槓escape,並且這樣的表達式能夠compose。好比:操作系統

(define reg1 (@... "/*" (@*(@!"*/")) "*/" ))
(define reg2 (@+ "foo"))
(define reg3 (@= "b"))

定義這三個表達式以後,咱們以後能夠用像(@... reg1 (@or reg2 reg3)) 這樣的表達式來鏈接3個不一樣的表達式,構造出更大的表達式。這樣的構造能夠無限的擴展。從這裏以及以往的經驗,我總結出一個廣泛適用的程序設計的教訓:儘可能不要把多個層次的語言「壓縮」到一層。咱們也看到正則表達式與「Unix哲學」有很大關係。我沒有考古,因此不知道孰先孰後,可是它們確定有直接的因果關係。二者都是Unix複雜性的來源。設計


This article was posted at yinwang’s sina blog,
on 2012-05-17.
Though it’s not available now.code

相關文章
相關標籤/搜索