官术网_书友最值得收藏!

  • 正則指引
  • 余晟著
  • 1953字
  • 2019-01-09 16:29:08

引子:關于正則表達式……

正則表達式這個名字看起來總有點古怪,概念似乎也不簡單,甚至需要用一整本書來講解;可是,它到底是什么呢?

身為技術人員,我相信你總會與字符串打交道,相應地,各種語言也都提供了與字符串有關的函數。不妨先看看下面幾個問題,字符串函數是如何解決的(下面的代碼使用Python語言,它很直觀,正文里有基礎的介紹。現在,你只需要知道def是定義函數的關鍵詞即可)。

1. 判斷字符ch是否數字字符

      def isDigit(ch) :
          return ch == "0" or ch == "1" …… or ch == "9"

2. 判斷字符串str是否電話號碼(為簡單起見,現在只考慮固定電話號碼,也就是長度在7~8位之間的數字字符串,且第一位不為0)

      def isPhoneNum (str) :
          if len(str) >= 7 and len(str) <= 8 and str[0] != "0" :
            for ch in str :
                if not isDigit(ch) :
                    return false
            return true
          return false

任務本身并沒有增加太多,但是程序復雜了很多倍;如果你不這樣看,那么,來個更復雜的。

3. 找出一段文本中所有的電話號碼

最直觀的辦法是,在字符串中的每個位置截取7~8個字符,調用之前的isPhoneNum()。這么做看起來沒問題,只是效率太低。

當然,很容易就可以做點改進,只在“當前字符為數字字符”的情況下調用isPhoneNum()。這樣效率倒是改進了,但是還有問題沒有解決:要求找到的是長度大于等于7個字符,小于等于8個字符的“數字字符串”,而不是“子字符串”——也就是說,假如數字字符串是64240000,需要將它找出來;如果數字字符串是13800138000,則需要忽略它,以及其中的任何子串(比如1380013800138000)。

所以,用isPhoneNum()找出字符串之后,還需要保證它之前的字符不是數字字符,之后的字符也不是數字字符。看起來很簡單,但為了避免越界錯誤,又需要判斷:如果當前字符是整段文本的第一個字符,則不需要判斷之前的字符,因為它不存在;同樣,如果找出的字符串在整段文本的末尾,則不需要判斷之后的字符,因為它同樣不存在……

到現在為止,即便只是找到最簡單的固定電話號碼,程序也非常復雜,難以維護。如果要查找的是形式更多變的文本,比如帶區號的電話號碼(021-64240000 或者03718888888)、手機號碼(13800138000或者+8613800138000或者013800138000),程序更是不可想象,更不用說文件路徑名、URL地址、電子郵件地址了!然而,日常開發中我們又確實經常需要面對這類任務,有什么更好的辦法呢?

正則表達式就是解決這類問題的萬靈藥。雖然許多人有點看不起它,覺得不入流,科班教材里也不會花太多篇幅來介紹它,但它確實是解決問題的利器——之前提到的三個例子,用正則表達式都可以輕松解決。

1. 判斷字符ch是否數字字符

      def isDigit(ch) :
          return re.search(ch, "[0-9]") != None

看起來很復雜,其實并不復雜:這里真正要關心的就是正則表達式[0-9],它表示“從0到9之間的任意字符”,很形象吧?re.search()是正則表達式運算函數,它判斷ch能否由正則表達式[0-9]匹配,可以則返回一個結果,否則返回None(這些細節正文中會講到)。

2. 判斷字符串str是否電話號碼

      def isPhoneNum(str) :
          return re.search(str, "[1-9][0-9]{6,7}") != None

這個正則表達式最開始是[1-9],表示第一個字符必須是1~9 之間的數字字符;之后是[0-9]{6,7},表示長度在6和7之間,由0~9之間的數字字符組成的字符串(兩部分加起來,整個字符串的長度在7和8之間)。要解決的問題復雜了,正則表達式仍然直觀形象。

3. 找出一段文本中所有的固定電話號碼

      def findNumStr(str) :
          return re.findall(str, '(?<![0-9])[1-9][0-9]{6,7}(?![0-9])')

這個正則表達式之前多出了(?<![0-9]),表示“之前不能是[0-9]”;之后多出了(?![0-9]),表示“之后不能是[0-9]”。雖然稍微復雜點,但意思明確,而且不難理解。re.findall()的意思也很明顯:找到所有這樣的字符串。

可以想象,循著這種思路,更復雜的電話號碼、手機號碼等任務都不難解決。更重要的是,之前需要許多行語句才能完成的任務,現在基本上只需要一個正則表達式,一條語句就可以完成。正因為如此,不少人雖然認為正則表達式不夠花哨、漂亮,卻不得不承認它是一種 “匕首應用”——匕首,沒有十八般兵刃那么大方,關鍵時候卻不可或缺,所以值得花時間練練。同樣,正則表達式雖然不能用來顯擺,但總有派得上用場的地方,花時間練練絕不是壞事。即便你的工作不是純粹的文本處理(比如日志分析),也總會有用到正則表達式的地方(比如查找和修改源代碼),所以我希望,這本書能陪伴你練出一身正則表達式的好功夫,在關鍵場合能亮出趁手的工具。

最后,為了尊重傳統教科書的習慣,附上正則表達式的“科班史”:

正則表達式發源于與計算機密切相關的兩個領域:計算理論和形式語言。20世紀40年代,兩位神經生理學家Warren McCulloch和Walter Pitts研究出一種數學方式來描述神經網絡的辦法,它們把神經系統中的神經元描述成小而簡單的自動控制單元。1956年,數學家Stephen Cole Kleene在他們研究的基礎上,發表了一篇名為“神經網事件的表示法”的論文,在其中,他采用了一些稱之為“正則集合(regular set)”的數學符號來描述神經網絡模型。

之后,UNIX的主要發明人Ken Thompson將這個符號系統引入了文本編輯器QED(意思是“在文本中搜索某種模式”),正則表達式由此也進入了計算機世界。隨后Ken Thompson又將正則表達式引入了UNIX下的文本編輯器ed,ed最終演化為大家熟悉的grep(grep得名自ed編輯器中的正則表達式搜索命令g/re/p,其中的re表示“正則表達式”)。

主站蜘蛛池模板: 同德县| 临桂县| 清河县| 利川市| 赤壁市| 会东县| 松滋市| 慈利县| 莆田市| 阿拉善左旗| 长治县| 铜川市| 巴彦淖尔市| 托克托县| 黔东| 林西县| 蚌埠市| 蒙阴县| 霞浦县| 清水河县| 和静县| 汉源县| 阜南县| 寿光市| 万山特区| 昆山市| 揭东县| 平舆县| 麻江县| 纳雍县| 吴忠市| 花莲市| 乐亭县| 漾濞| 古浪县| 法库县| 孟州市| 罗江县| 鄂温| 和静县| 宕昌县|