動動手來學 Regular Expression 正規表達式

#Regex #正規表達式
Roy
技術文章
動動手來學 Regular Expression 正規表達式

\[a-zA-z]{3,}|^\D$|^[0-9a-z]*$|^[0-9A-Z]*$\

相信大家在寫程式的過程當中,一定都曾看到過像上面這種,有點像亂寫的程式碼,有些可能看起來沒那麼複雜,而有些則是長到讓人難以招架,光是看到就讓人頭痛,更不用說去讀懂它了。

事實上,這種程式碼叫做正規表達式(RegExp,Regular Expression),是處理文字非常重要的利器,當你希望規範文字的格式、或者是限制使用者的文字格式時,正規表達式是不可或缺的存在。

文首的表達式是我超級討厭的銀行密碼格式(還有 PlayStayion Network 的密碼格式),會找出這些格式:

  • 超過連續三個英文字母(不分大小寫)
  • 沒有任何數字
  • 沒有任何大寫字母
  • 沒有任何小寫字母

只要你設立的密碼符合以上任何一個格式,程式就會拒絕你使用這組密碼,以這樣的方式來讓使用者建立出一個安全性相對高的密碼——雖然很多時候都是徒勞無功的。

在這篇文章當中,希望可以透過簡單明瞭的方式來講解正規表達式,並且讓大家在未來撰寫或閱讀正規表達式的時候,能夠得心應手,無所畏懼。

在 Ruby 裡面,有幾種方式可以定義正規表達式,比較普遍的方式是用 slash(/) 將正規表達式包起來,像是 /[a-zA-Z]*/,而另一種方式則是用 Ruby 的 class生成,變成、 Regexp.new('[a-zA-Z]*') ,值得注意的是,使用第二種方式不需要加入 slash,只要將內容以字串方式傳入即可。大部分情況下我們會看到第一種方式,少部分的情況下,特別是需要動態產生正規表達式的時候,第二個方式會比較好用。最後則是將利用 %r{[a-zA-Z]*} 這樣的方式也可以定義正規表達式。

使用 %r 的情況下,不需要將 {} 裡面的表達式包成字串

在正規表達式當中,我們有幾個比較常用的 Match Character 來表示我們的規則:

Match Character 規則
. 所有字元
\w 所有字母,包含大小寫跟數字
\d 所有數字(0-9)
\D 除了數字以外的字元
[ ] 指定的範圍,例如[a-f]

當然,只有 Match Character 還不夠,我們還必須指定字元是否重複,或者重複的次數,否則預設都是一個字元。

重複表示 規則
? 未出現或出現一次
* 未出現或出現多次
+ 出現一次或多次
{a} 出現 a 次
{a,} 至少出現 a 次以上
{,b} 最多出現 b 次
{a,b} 最少出現 a 次,最多出現 b 次

舉個例子來說,台灣的市話號碼有可能是七碼或者八碼,我們就可以利用 {a, b} 這個重複規則來做出正確的格式:/^\d{7, 8}$/。這個正規表達式的意思,我們希望找出一個有七到八個數字的字串,而前面的 ^ 以及後面的 $ 分別代表字串的開頭開始和字串的結尾,這在很多場合是非常重要的。假設沒有規定必須從字串的最前面開始或在最後結束,他便會抓取最前面或是最後面符合的字串,如此一來便不符合我們希望的規則。

舉例來說,如果我們的規則是這樣 /^\d{7, 8}/,沒有最後結束的符號,那麼即使當我輸入手機號碼 0955555555,他還是會回傳最前面的八位數字給你。

# incorrect
pry(main)> '09555555555'.match /^\d{7,8}/
=> #<MatchData "09555555">

# correct
pry(main)> '09555555555'.match /^\d{7,8}$/
=> nil

如此一來我們可以回頭看一開始的表達式

\[a-zA-z]{3,}|^\D$|^[0-9a-z]*$|^[0-9A-Z]*$\

這邊可以注意到,中間我們用 pipe | 來做分隔,其實 pipe 就是 or 的意思,我們可以利用這個方式來加入多個條件,因此我們也可以將表達是分開來解讀。

  • [a-zA-Z]{3,} 這個部分則表示我們要找出無論大寫或是小寫的字母,三個連續字母連在一起的組合。
  • ^\D$ 這裡則表示找出整個字串都完全沒有數字的組合。
  • ^[0-9a-z]*$ 這段則是找出整個字串中只有小寫和字母,意思就是字串中完全沒有大寫的組合。
  • ^[0-9A-Z]*$ 和上面那段相似,只是找出沒有小寫的組合。

利用這個表達式,我們就可以順利做出一個討人厭的密碼規則囉,是不是很簡單呢?

在文章的最後,提供一個方便的線上工具,你可以在這個網頁上任意嘗試各種 RegExp 的組合,並且確認規則是否正確,非常地方便:http://rubular.com/

希望透過這篇文章,讓大家可以輕鬆地解讀正規表達式,並且也能夠順利地用正規表達式寫出簡潔的程式碼。

photo by Christiaan Colen