Perl 常用的 regexp 規則列表
我們小時候學中文, 從來就不是從文法學起, 而是聽說讀寫很多例句, 腦中自然歸納出一些 (可能自己都說不太上來的) 規則。 聽說這樣的學習方式比較自然, 效果比較好; 當然, 花費的時間也相對較長。
筆者那個年代, 英文是從國中才開始學起, 已經不像小朋友有那麼充裕的時間。 因此老師/課本/參考書免不了要幫我們整理文法以加速學習: 什麼八大詞類, 直述句/疑問句, 假設語氣, 從屬子句, ... 等等。
如果您還記得 「長線投資的電腦學習策略」 裡面提到: 今日的資訊教育實在應該以英文數學教育為師, 就能理解為什麼要談這些了。 筆者認為像是 regexp 及命令列這類東西具有長遠投資價值, 但因為要記的文字內容較多而稍嫌困難, 應該可以用學中文的方式來教/學。 所以前兩篇的重點不在有系統/有組織, 而在令讀者熟悉/培養感覺。 不過 regexp 較之中英文, 簡單很多, 如果想把重要的符號及語法用有系統/有組織的方式列出來, 其實也並不會太嚇人。 這篇的目的就是要給那些與我學英文有相同 (不太好的?) 習慣, 迫不及待想看清楚規律的讀者看的。 但請記住: 沒有看過例子的規則, 其實都不算真的學過。 所以看這篇時, 請把它當做像英文文法書一樣, 用來複習/預習/查詢, 並且在腦中多回想/設想例子; 請不要認真地死背所有規則。 有些沒有教過的東西, 後面的章節會詳述。
一、 常用符號
常用的 regexp 符號可以大致分為三類:
- 比對 「一個字元」 的符號:
[
...]
... 當中任何一個字元[^
...]
除了 ... 之外的任何一個字元.
任何一個字元
- 具有 「定位」 功能, 但本身不吃掉任何字元的 anchor:
^
... 以 ... 開頭的字串- ...
$
以 ... 結尾的字串 \b
文數字/非文數字 的邊界。
- 計數用, 表達 「前面的樣版重複出現多少次」 的
quantifier:
{5}
重複 5 次{3,7}
重複 3 到 7 次{3,}
重複至少 3 次?
可有可無。 相當於{0,1}
*
重複出現任意次, 包含 0 次。 相當於{0,}
+
重複出現任意次, 至少 1 次。 相當於{1,}
Quantifiers 有 "不貪婪版本": 例如 +? 是 + 的 "不貪婪版"; *? 是 * 的 "不貪婪版"。
小括弧 (
...)
有三個常用的效果: 第一,
它讓 quantifier 可以作用在一串符號上,
而不只是作用在一個字元上。
第二, 小括弧裡面可以用 |
切成幾段, 表達 「這個字串或那個字串」。
(...|...|...)
可以說是
[...]
的升級版。
第三, 小括弧裡面的東西, 可以用 $1、 $2 撈出來用。 (下詳)
二、 為何選 perl?
我們之所以選擇 perl 來教 regexp, 有好幾個原因。 第一, perl
有一個很容易記的規則: 凡是標點符號, 加上倒斜線,
一定沒有特殊意義。 像 grep 或 sed 的 regexp
就有點複雜。 (所以我用 grep 時, 喜歡用
grep -P ...
採用 perl 相容模式,
腦袋比較不會打結。)
第二, perl 替最常用的 [
...]
定義了簡寫:
\d
其實就是[0-9]
, "任何一個數字"\D
其實就是[^0-9]
, "任何一個非數字"\w
其實就是[a-zA-Z0-9_]
, "任何一個文數字"\W
其實就是[^a-zA-Z0-9_]
, "任何一個非文數字"\s
其實就是[ \t\n]
, "任何一個空白類字元"\S
其實就是[^ \t\n]
, "任何一個非空白類字元"
這裡有關 \w 與 \W 的說明並不嚴謹. 若要處理英文以外的西方語言, 請參考 perlre(1) 與 perllocale(1)。
第三, 後來很多其他軟體都宣告支援 PCRE, 也就是 Perl-Compatible Regular Expressions, 可以說 perl 的 regexp 已成為軟體界共同的標準。
第四, 請見下一節。
三、 三種常用句型
perl 的彈性很大, 小小的變化就可以造出三種不同的句型, 應付常用的搜尋/代換工作 (實際上 一句 perl 能夠寫出的簡單變化還多得是; 不過為了怕嚇到讀者, 筆者必須克制一下, 就此打住):
- 搜尋, 並印出整列:
perl -ne 'print if /.../'
(簡略寫法)
或是正式寫法:perl -ne 'print if m/.../'
(m 表示 "match" 比對)
效果約略等同於grep -P '...'
這裡的 -P 就是 perl-compatible 的意思。 - 搜尋, 精確列印 (不要前後文):
perl -ne 'print "$1\n" if /..(..)../'
效果約略等同於grep -Po '..'
(但 grep 無法 print 指定僅印比對字串的一小部分) - 代換:
perl -pe 's/.../.../g'
效果約略等同於sed 's/.../.../g'
。 但 sed 不支援 PCRE, 所以我只有很簡單的情況才用 sed。
這裡的 $1 表示 "第一對小括弧裡面的東西", $2, $3, ... 類推。
在 "搜尋, 精確列印" 句型, 及 "代換" 句型當中,
可以用來指稱比對到的字串的一部分, 再把它的變形印出來。
例如 ip.txt
這個文字檔記錄了一些套件的大小, 類別, 名稱三個欄位的資訊。
想把第二與第三個欄位對調過來, 可以用 "搜尋, 精確列印" 句型:
perl -ne 'print "$1 $3 $2\n" if /(\W+)\s+(\W+)\s+(\W+)/'
ip.txt
也可以用 "代換" 句型:
perl -pe 's/(\W+)\s+(\W+)\s+(\W+)/$1 $3 $2/'
ip.txt
當然如果資料有點不整齊, 像 這樣, 那就只能用 "代換" 句型了。
四、 其他常用技巧
在比對語法 m/.../
或代換語法 s/.../.../
後面, 都可以加上一些選項, 微調比對或代換的效果:
- i 忽略大小寫 (ignore)
- g 整列全面代換 (global)
如果你的 regexp 裡面正好用到 / 那麼也可以改用其他符號作為分隔符號,
但這時代表 「比對字串」 的 m 就不能省略。 例如 m#...#
或 s|...|...|
也都可以。
五、 進階: 段落模式
通常 regexp 以列為單位在處理資料。 如果每一筆資料橫跨好幾列, 那就要用 -000 選項叫 perl 進入 「段落模式」 (paragraph mode), 把資料視為 「以空白列分隔的許多段落」。 準備資料時, 要在段落之間插入空白列; 同一段落的資料裡面不能有空白列。
在這個模式下, . 不會比對到換列字元。 如果希望它比對到換列字元, 需要加上 s 選項。
在這個模式下, ^ 跟 $ 分別比對到 「整段的頭」 跟 「整段的尾」。 如果希望它們比對 「一列的頭」 跟 「一列的尾」, 需要加上 m 選項。
- 本頁最新版網址: https://frdm.cyut.edu.tw/~ckhung/b/re/rules.php; 您所看到的版本: August 30 2018 15:24:37.
- 作者: 朝陽科技大學 資訊管理系 洪朝貴
- 寶貝你我的地球, 請 減少列印, 多用背面, 丟棄時做垃圾分類。
- 本文件以 Creative Commons Attribution-ShareAlike License 或以 Free Document License 方式公開授權大眾自由複製/修改/散佈。