軟體工程/遊戲設計/隨筆
subtitles 正規表達式 access_time 2019-05-14

Google Form(Google表單,以下簡稱GF)是非常好用的雲端問卷工具,被普遍用於民意調查、揪團、線上抽獎等⋯⋯用途。然而,讀者或許也有這樣的經驗:收集來的資料往往格式不一,舉例來說:光是手機號碼,就有可能包含了各種千奇百怪的格式:

  • 0912-345-678
  • 0912345678
  • 0912 345 678
  • 8860912345678
  • 886-912-345-678
  • 0912345678(十點以後別打因為我睡啦)

儘管我們可以在問題的說明中加上「請使用0912-345-678格式」的說明。然而,在缺乏良好的使用者介面的指引下,這樣的道德勸說是無法讓使用者配合的。格式混亂的結果就是:資料收集者必須在事後花大量的精神來進行資料清理(Data Cleaning)。

本篇文章要介紹GF比較少為人知,但又非常實用的功能正規表達式。透過正規表達式的設定,GF可以比對用戶輸入的資料是否為理想的格式——如果錯誤,則在介面中顯示錯誤訊息與警告,提醒用戶修正輸入的資料格式。

pic 圖片:正規表達式可以讓Google Form拒絕格式不正確的資料,並提供錯誤指引。

什麼是正規表達式?

正規表達式(Regular Expression)是電腦科學領域用於字串比對與搜尋的標準語法。程式設計師可以透過正規表達式,來比對字串是否符合預期的規範,並進行各種處置工作。例如拒絕表單提交、顯示錯誤訊息⋯⋯等。

來看個範例最快。

第一個範例:百位數

^[1-9][0-9]{2}$

作為一個正規表達式,上述的範例代表了一個百位數的正整數(例如365)。這串編碼所傳達的意義為:開頭必須為1-9其中一個數,接著必須為兩個0-9之間的數字,然後結尾——這些條件,最後就會構成一個標準的百位數。

透過這樣的解釋,讀者或許已經可以猜測出來:^代表開頭$代表結尾[]代表一個接受的數字的集合。事實上,正規表達式有非常多的語法,可以用來組合成世界上各種可能的資料規範。它可以寫到非常非常複雜,舉例來說,我們可以透過搜尋「email regular expression」,來找到這麼一段正規表達式:

/^(([^<>()\[\]\\.,;:\[email protected]"]+(\.[^<>()\[\]\\.,;:\[email protected]"]+)*)|(“.+"))@((\[[0-9]
{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]
{2,}))$/

這一段很像貓踩到鍵盤噴出來的亂碼,它其實代表著一段符合主流規範的email格式——它包含了前段的username、小老鼠、以及後段的網域名稱。當某網站要求使用者以email註冊帳號時,就可以用這樣的正規表達式來確保用戶輸入的資料是否正確。

儘管看起來很嚇人,但我們只要懂得基礎的邏輯,並學會製作出一些簡單的用途即可。對於大部分複雜的資料規格,我們都可以透過像這樣的搜尋「台灣身分證字號, 正規表達式」,來找到他人提供的可靠的寫法直接套用。

pic 圖片:常見的註冊表單。透過瀏覽器前端語言Javascript與正規表達式,來判斷用戶輸入的資料是否正確。這樣的互動式表單不僅提高了使用者體驗,讓資料的可靠度大幅提升,更能透過在用戶端預先過濾掉非惡意的錯誤,為伺服器與資料庫省去不少工作。

Google Form如何套用正規表達式?

要在GF中套用正規表達式非常簡單,只要是簡答或是段落的問題型態,都可以透過右下角的選單找到Data Validation啟動並進行設定。

值得注意的是:GF的正規表達式包含了兩種比對模式

  • Matches:意即整個字串完全符合才通過檢查。在這個模式下,1234並不會通過我們剛剛寫出的百位數字的檢查(本篇文章只以Matches作為範例)。

  • Contains:意即字串中部分包含即可通過檢查。在這個模式下,1234通過我們剛剛寫出的百位數字的檢查。

pic 圖片:在GF設定正規表達式檢查。

作為範例,筆者也編寫了一份【Demo版問卷】,讀者可以直接至連結中體驗應用的效果。

正規表達式怎麼寫?

如果你需要的正規表達式在網路上找不到怎麼辦呢(舉例來說:某間學校特殊的學號格式?)那就只能自己寫囉!以下將會介紹正規表達式基本的用法,如果不是要太複雜的格式,這些知識通常在GF當中就已經足夠。

語法之一:結尾/起始/集合

^ :起始符號[1]
$ :結尾符號
[]:群集符號

例句1

^[br]:開頭必須是b或是r。

例句2

[0123456789]$:結尾必須是數字,同義於[0-9]$

例句3

^[a-zA-Z]$:開頭只能是一個大小或小寫的英文字母,然後結束。

目前看起來沒有什麼實用價值對吧?但是只要結合更多語法,就能做出各種派得上用場的組合了。

語法之二:數量標示

用以標示某個符號或集合被允許出現的次數。

{}   :數量符號
{10} :代表正好10個
{10,}:代表10個或以上
{1,3}:代表1~3個之間

例句1

^[0-9]{10}$:開頭到結尾都必須是數字,而且正好10個。在這個條件下,手機號碼0912345678可以通過這個檢查。使用0912-345-678或是886123456789,都是不會過關的。

例句2

假設有一所大學的學號是b開頭+9碼數字(例如:b123456789),如果要使用問卷收集同學的正確學號(皆為b開頭),該怎麼寫呢?

答案就是^b[0-9]{9}$

例句3

同樣的道理,如果學號有可能是b或r開頭,b代表大學部而r開頭代表研究所呢?

其實也非常簡單:^[br][0-9]{9}$

語法之三:縮寫

對於幾個常用的集合以及數量標示,正規表達式也提供了常用的縮寫。要注意的是\符號是Enter鍵上方的反斜線,與數字鍵上方的正斜線/是不同的。儘管使用縮寫不是必要的,但是如果要閱讀網路上各種分享的正規表達式,還是必須要了解縮寫的意義[2]。

  • \w同義於[a-zA-Z],代表所有的大小寫英文字母形成的集合
  • \d同義於[0-9],代表所有數字形成的集合
  • +同義於{1,},代表出現一次或以上
  • ?同義於{0,1},代表出現一次或零次
  • *同義於{0,},代表出現零次以上

語法之四:跳脫字元(反斜線)

反斜線\除了用於字集的縮寫之外,它還代表跳脫字元的意思。對於已經被正規表達式歸納為有特殊意義的符號來說,如果需要使用符號的本身,就必須使用反斜線來跳脫它的特殊意義[3]。

例句

^\+(886)-\d{3}-\d{3}-\d{3}$:這個例子會檢查出一個+886-123-456-789的手機號碼。其中+符號已經被正規表達式規定為數量符號的縮寫了。因此,當我們要使用+符號本身時,就必須使用\+來表達我不需要+代表的特殊意義,我只要+的本身

語法之五:小括號

括號中的項目會優先被處理,在處理較複雜的正規表達式時很常使用。一般GF實務上倒是不太常用到,在此淺談即止。

例句

(ha){2}$:字串的結尾必須是haha,這通常是聊天時尷尬的象徵。

語法之六:或

例句

^(IM|EE|CS):開頭必須是IM或是EE或是CS(資管、電機、資工)。這個案例可能出現在要求同學回報自己選擇的課程編號時使用到。

練習

想要快速掌握正規表達式的技巧,別忘了自己動手寫寫看。以下這個練習可以應用到大部分前述的技巧,不妨一試!

請寫出西元生日的(例如:1987/6/25)的正規表達式

  • 別忘了都有可能是個位數或十位數 。
  • /這個符號必須被跳脫
  • 想寫複雜一點,還可以檢查年月日的開頭第一碼。

正規表達式沒有標準答案,端看設計者希望驗證到多詳細囉,以下為兩種參考答案。

  • 簡單版:^\d{4}\/\d{1,2}\/\d{1,2}$(只檢查數字與分隔符號)
  • 複雜版:^[1-9]\d{3}\/[1]?\d\/[123]?\d$(額外限制合理的數字範圍)

要怎麼測試正規表達式呢?

第一種方式,是使用線上工具,例如regexr.com這個網站。使用方法非常簡單,只要輸入你的正規表達式,並且在下方輸入可能的字串,就可以知道運作方式是否符合自己的預期囉。當然,也可以直接寫在Google Form當中,再自己試著填寫表格,看看效果是否符合。

pic 圖片:regexr.com可以協助我們測試正規表達式

結語

Google Form提供了優異的操作介面與強大的表單功能。配合上正規表達式,就可以讓我們的問卷收集到更乾淨的資料,也大幅降低資料清理的工作量啦。

註解

[1] ^在集合中還代表否定之意,舉例來說[^abc]代表[abc]的否定。為了避免干擾學習效果,因此只在註解中說明。

[2] 使用大寫的字母代表差集,例如\d代表著[1-9]\D則代表著[1-9]

[3] 完整地說,反斜線代表著切換特殊字符普通字符的意義——在特殊字符前時,將之跳脫為普通字符;而在普通字符前時,則將之跳脫為特殊字符。舉例來說,當使用\d代表[0-9]時,\本身也是一個將正常的字符d跳脫特殊字符