用十分鐘學會字串處理的那些事兒
( 關於 Regular Expression 的那些常識與密技 )
陳鍾誠
2017 年 6 月 22 日
程式人《十分鐘系列》程式人《十分鐘系列》
本文衍生自維基百科
寫程式
●看起來好像很難!
但是如果不要去考慮
●那些圖形化的介面!
那麼、程式的功能
●通常只剩下這些:
–1. 讀檔 (=> 字串 )
–2. 字串處理
–3. 寫檔
這學期
● 我有一門《系統程式》的課
● 第一個作業是要學生們寫組譯器
● 很多學生就這樣掛了!
組譯器
●其實就是:
–1. 讀檔 ( 組合語言程式 )
–2. 字串處理 ( 轉成 0 與 1)
–3. 寫檔 ( 輸出機器碼 )
以下是組譯器執行的輸出入
輸入輸出
如果你看主程式
● 會發現讀檔後用 split 就可以把組合語言分割成一行一行的
如果你仔細看
●會發現個奇怪的東西
–asmText.split(/r?n/)
那個 /r?n/ 是什麼東東阿?
喔!
●那是 Regular Expression
( 正規表達式 )
如果你去網路查
Regular Expression
可能會看到一堆很可怕的東西
基本上就像是某種外星文字
像是這個
這個
還有這個
然後
就沒有然後了!
由於不知道該怎麼學
所以就學不會程式
只好後悔自己念了資工系
但是也已經來不及了!
不過請注意
程式領域的說明文件
● 其實不是用來教你怎麼寫程式的
● 而是讓你用來《查》的!
初學者看不懂是正常
看得懂是異常!
學正規表達式也是如此
先不要去看一堆手冊
而是好好的
●看幾的例子就行了!
讓我們用 Chrome 瀏覽器
●的開發人員工具
●來示範正規表達式的用法!
首先選取 Chrome 瀏覽器功能表中的
《更多工具 / 開發人員工具》
在 console 裡面試試這些
你應該會發現
● 只要用 [0-9]+ 就可以比對
《數字串》
● 然後用 [a-z]+ 就可以比對
《小寫英文字母串》
所以才會看到
● 'this is a book'.match(/[a-z]+/g)
– ["this", "is", "a", "book"]
● 'my phone number is 082-313534, call me! '.match(/[0-9]+/g)
– ["082", "313534"]
在 JavaScript 當中
●只要是 /…/ 括起來的那些
通常就是正規表達式。
JavaScript 的正規表達式
● 支援一些模式參數,像是:
– g : (global) 全域比對
● 如果沒加 g 只會比對第一個
– i : (ignore case) 大小寫不區分
– m : (multiline) 多行比對
所以下列兩者的結果不同
● 'this is a book'.match(/[a-z]+/g)
– ["this", "is", "a", "book"]
● 'this is a book'.match(/[a-z]+/)
– ["this", index: 0, input: "this is a book"]
● 是因為差了一個 g 參數的原因!
在正規表達式裏
● 如果常常要寫 [0-9] [a-z] [A-Za-z0-9]
這些東西的話,會把式子搞得很長很複雜
● 所以我們可以用 d 代表 [0-9]
● 或者用 w 代表 [0-9A-Za-z_]
這就是大部分說明文件裏寫的那些
其實你只要理解
● [0-9] 代表 0 到 9 的這些數字
● [aeiou] 代表這五個字母的集合
● [^0-9] 代表不是數字的字元集
● [^aeiou] 代表不是 aeiou 的字
然後歸納出
● [...] 代表那些括號內的字元
● [^...] 代表不要那些括號內的字元
接著理解
●+ : 代表出現 1 次以上
●* : 代表出現 0 次以上
●? : 代表出現 0 次或 1 次
你就可以知道
● [0-9]+ 代表數字串
● [a-zA-Z]+ 代表英文字母串
● [0-9]+-[0-9]+ 可以比對像
082-313534 這樣的電話號碼
但是如果你要更精準的
指定字元的重複次數範圍
● 就可以用
– [0-9]{2,4}-[0-9]{5,9}
● 來指定
– 區碼有 2 到 4 碼,主碼有 5 到 9 碼
當您開始熟悉上述這些東西之後
●就可以進一步記住
–[0-9] = d
–[A-Za-z0-9_] = w
還有那些特殊字元
● n : 換行 (newline, ASCII = 0x0a)
● t : 跳格 (tab)
● r : windows 中常接著 n 一起代表換行
(ASCII = 0x0c)
● s : 空白型字元 = [ fnrtv]
另外還有幾個要記的特殊符號
● . : 代表任何字母 ( 換行除外 )
● ^ : ^ 不在 [...] 裡面時,代表字串首
– 例如 /^[0-9]/ 代表字串必須以數字開頭
– 但是 /[^0-9]/ 代表不是數字
● $ : 代表字串結尾
然後記住一條規則
● X 代表不是 x 的那些字母
● 例如:
–D 代表不是 d 的所有字母
–也就是 [0-9] 以外的那些字母
接著知道
● 括號 (...) 可以括起一個群組
● 用 (xxx)|(yyy) 代表兩者都可以比對
● 像是 ((dog)|(cat))xxx
代表 dogxxx 或 catxxx
然後你可以用
● $1, $2, … 取得對應的群組
● 例如: 'tel:082-313534'.match(/(d+)-(d+)/)
– 結果為 ["082-313534", "082", "313534"]
– 其中 $1 是 082, $2 是 313534
– 而 $0 代表比對到的完整字串 082-313534
必須注意的是
● 群組的號碼是以左括號出現次序為準的
● 所以 ab(cd)(ef(gh)) 當中
– $1 是 (cd)
– $2 是 (ef(gh))
– $3 是 (gh)
– $0 是全部 ab(cd)(ef(gh))
另外當 ? 出現在 *,+,{m,n} 後面時
● 代表非貪心模式 ( 而不是 0 次或 1 次 )
● 也就是盡可能比對最短的
● ( 預設是貪心模式,會盡可能比對最長的 )
● 例如 o+ 會比對到 oooooxxx 中的
ooooo ,但是 o+? 則只會比對到 o
剩下還有一些群組組合
《正向 - 反向、肯定 - 否定》
● 算是比較進階的操作
最後請注意
● 正規表達式是很強力的工具
● 但很多時候我們只需要很簡單的方式
去處理字串
● 這時候常常只需要使用 split 之類的
功能就行了!
像是要把字串用空白分割
就可以用 split(' ')
● 例如: 'abc def ghi'.split(' ')
– 結果為: ["abc", "def", "ghi"]
但如果更複雜一點
●就要用正規表達式來分割
除了比對函數 match 之外
●還有 replace 函數也很好用
像是這樣
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/String/replace
這樣
這樣
還有這樣
這些字串處理的事情
● 可以說是現代程式人的基本功!
畢竟命令列程式最常做的事
●就是
–1. 讀檔
–2. 字串處理
–3. 寫檔
而使用正規表達式
●是幫助你用一行表達式
抵掉百行程式的快速方法!
身為程式人
● 這似乎是一個非常值得投資的基本技術!
您說是嗎?
這就是我們今天的
十分鐘系列
希望您會喜歡!
我們下回見!
Bye Bye!

用十分鐘學會字串處理的那些事兒!