Regular Expression
正規表達式 (RE, Regex, Regular Expression) 是一種擷取規則文字的機制。其原理為:
- 建立文法 (Grammar) 表達式。
- 演算法根據其規則進行比對 (Match)。
- 若相符,將會把文法中的群組 (Group) 內容取出,稱為 Token。
- 若不相符,會跳過。
- 繼續尋找直到搜尋完畢。
在閱讀 Syntax 之前,可以先透過本節學習如何從有規律的文字中擷取需要的內容。
本節使用 JavaScript 表示 RE 文法,故會有夾注號 /
如:/re+/
。使用其他程式語言時可忽略,按其規則為準。
推薦的測試網站:https://regex101.com
Rules
Base Grammar
除了特殊字元,RE 的文法等價於要解析的文字:
/abc/ // abc
特殊字元,作為起始即有特殊作用:.
, ?
, +
, *
, |
, [
, (
, )
, $
, ^
, \
非特殊字元,可單獨作為表示,但是可配合特殊字元:,
(comma), {
, }
, ]
Any Single Character
使用 .
表示任意單一字元,包含不可視字元。
Escape Character
跳脫字元 (Escape Character) 為反斜線 \
。
主要可以表示特殊規則,幫助縮減文法長度。
另外可以強迫特殊字元(包含自身)變成普通文字。
Invisible Characters
不可視字元 (Invisible Characters) 為顯示或列印時沒有顯示符號的字元,如下:
符號 | 說明 |
---|---|
| 空白 (Space) |
\t | 縮進 (Tab) |
Linux & MacOS: \n , Windows: \r\n | 換行 (Newline) |
Repetition
單一文字重複規則如下:
/a{n}/ // 表示 a 重複 n 次
/a{n,}/ // 表示 a 最少重複 n 次,最多無限次
/a{n,m}/ // 表示 a 最少重複 n 次,最多 m 次
而簡化語法如下:
/a?/ // 表示 a 最少重複 0 次,最多 1 次(可寫可不寫)
/a+/ // 表示 a 最少重複 1 次,最多無限次
/a*/ // 表示 a 最少重複 0 次,最多無限次
若前一個是群組則會套用到群組的規則。
/(abc)+/ // abc, abcabc, abcabcabc, ...
Group
使用群組 (Group) 功能擷取文字,符號為 (
和 )
。
定義 Group 0 為整段符合的文字,剩下的從 1 開始編號,重複的以此類推。
/(aaa)(bbb)/ // g0: aaabbb, g1: aaa, g2: bbb
/(aaa)(bbb)+/ // g0: aaabbbbbb, g1: aaa, g2: bbb, g3: bbb
若沒有要擷取文字,為了方便定義規則,可以宣告為匿名群組。
/(?:abc)+/
Logical Or
邏輯或 |
運算子能夠將多個類似的規則在同個文法中表現。
然而正規表達式的邏輯或運算子是最低優先權的,必須使用匿名群組等提升優先權。
/aa(?:bb|cc|dd)e/ // aabbe, aacce, aadde
List
清單 (List) 表示單一字元的多種選項,是邏輯或的一種縮寫,符號為 [
和 ]
。
/a[bcd]e/ // abe, ace, ade
/a(?:a|b|c)e/ // 等價
為了方便,清單支援 ASCII Code 的範圍 (Range) 標示,且可以連續標示。 不過如果是其他字元,會以 ASCII 的編號表示。
/[0-9]/ // 0 ~ 9
/[a-zA-Z]/ // a ~ z, A ~ Z
另外清單前端加上 ^
符號為反向集合,會對應到沒有顯示的字元,可以只標示例外即可。
/[^a-z]/
而清單也支援重複的定義。
/[qrs]{3}/ // qqq, rrr, sss, ...
Anchor
定位點 (Anchor) 可以幫助在文字中定位,不代表任何字元,常用的如下:
^
:整個字串或行的開頭。$
:整個字串或行的結尾。
Common Tokens
以下列舉常用的規則:
符號 | 說明 | 等價 | 反向 |
---|---|---|---|
\s | 不可視字元 | [\r\n\t ] | \S |
\d | 數字 | [0-9] | \D |
\w | 文字 | [0-9a-zA-Z] | \W |
\b | 文字邊界(定位點) | (^\w\|\w$\|\W\w\|\w\W) | \B |
Flags
正規表達式的選項稱為 Flag,可能因為不同程式語言的規則而異。常用的如下:
Flag | 說明 | 備註 |
---|---|---|
global | 不只作用一次 | Python 不支援 |
multiple line | 啟用後,^$ 對應行的開頭和結尾,而非整個字串 | |
insensitive | 忽略大小寫 | |
extend | 啟用單行註解功能 # 並忽略空白標示 | JavaScript 不支援 |
stiky | 每次符合必須相連 | 僅 JavaScript 支援 |
unicode | 支援萬國碼,\w 將對應所有其他字元 | |
ascii | 僅支援 ASCII Code | 僅 Python 支援 |
在 JavaScript 中,Flag 可以寫在括弧後啟用。
/abc/gm
Token Example
試著思考題目,並參考正規表達式的撰寫思路。
CSV
逗號分隔值 (CSV, Comma-Separated Values) 通常為逗號、縮進、空白等符號分開數值。
0.01 0.06 -9.997
1.69 +40.44 4.6321
思路:
-
數字在文法中可以表示成:
/d+/
-
浮點數可以表示成:
/\d*\.?d+/
-
帶有符號的浮點數可以表示成:
/[+-]?\d*\.?d+/
-
每行有三個數值,用空白分隔,可以表示成:
/^([+-]?\d*\.?d+) ([+-]?\d*\.?d+) ([+-]?\d*\.?d+)$/m
可得:
Match | g1 | g2 | g3 |
---|---|---|---|
1 | 0.01 | 0.06 | -9.997 |
2 | 1.69 | +40.44 | 4.6321 |
G Code
假設這是某台工具機的指令碼,你必須設計它的速度規劃。 在忽略換行記號的情況下,試著抓取以下每行 G code 中的 G 碼、座標和進給速度。 不相關的內容可以忽略。
%
O100
G00 G40 G49 G20 G90
N05 M10
N10 M05
N15 G91 G28 Z0
N20 G90
N25 T1 M06
N30 G00 Z.1
N35 G00 X2.5 Y2.5
N40 G01 Z-.25 F200
N45 G01 X5 Y5
N50 G00 Z.1
N55 G28 X0 Y0
N60 M05
N65 M45
%
-
根據 N、G、X、Y、Z、F 排列順序,每個符號會跟隨一個整數或帶符號的浮點數。
/N\d+/ /X[+-]?\d*\.?\d+/
-
而 N、X、Y、Z、F 為可選的項目。
/(?:X([+-]?\d*\.?\d+))?/
-
可以使用
\s*
代表間隔的空白,每行可排列成:/G(\d+)\s*(?:X([+-]?\d*\.?\d+))?\s*(?:Y([+-]?\d*\.?\d+))?\s*(?:Z([+-]?\d*\.?\d+))?\s*(?:F([+-]?\d*\.?\d+))?\s*/
可得:
Match | g1 | g2 | g3 | g4 | g5 |
---|---|---|---|---|---|
1 | 00 | ||||
2 | 40 | ||||
3 | 49 | ||||
4 | 20 | ||||
5 | 90 | ||||
6 | 91 | ||||
7 | 28 | 0 | |||
8 | 90 | ||||
9 | 00 | .1 | |||
10 | 00 | 2.5 | 2.5 | ||
11 | 01 | -.25 | 200 | ||
12 | 01 | 5 | 5 | ||
13 | 00 | .1 | |||
14 | 28 | 0 | 0 |