Regular Expression
因為單純想要讓自己工作上有比較方便的文件可以參考,所以這篇都以 Ruby 或者 bash 的寫法來作為例子
Basic Operation
特殊符號
代號 | 代表 | |||
---|---|---|---|---|
\w | [0-9a-zA-Z_] 英文數字或底線 | |||
\d | [0-9] | |||
\s | white space (tabs, regular space, newline) | |||
\W | not in [0-9a-zA-Z_] | |||
\D | anything that’s not a number | |||
\S | anything that’s not a space |
其他特殊符號
這要怎麼用呢? 比方說我現在要把文件裡面所有用到數字的地方挑出來:
1 | grep -n '[0-9]' regular_express.txt |
量詞 {}
如果想要比對連續的相同規則,可以用 {}
1 | # 使用 {5} 表示連續出現 5 次 |
量詞還有幾個更常用的特殊字元:
*
出現 0 次以上,等於{0,1}
+
出現一次以上,等於{1,}
?
出現 0 次或 1 次,等於{0,}
中括號 []
不管中括號裡面有幾個字,最後都只代表一個字元
範例:
1 | > grep -n 't[ae]st' regular_express.txt #[ae] 代表 a 或 e |
如果我們要選的是所有大寫的 A B C…Z,就可以直接用 [A-Z]
如果要選的這一字元是所有數字跟英文,就可以使用 [A-Za-z0-9]
反向選擇 跟 行首字元^
反向選擇範例(在 []
裡面)
1 | > grep -n 'oo' regular_express.txt |
行首字元範例 (不在 []
裡面)
1 | > grep -n '^[a-z]' regular_express.txt |
行尾字元 $
1 | > grep -n '\.$' regular_express.txt |
任意一個字元 .
1 | > grep -n `g..d` regular_express.txt |
Group
group 是拿來捕獲特定文字供後續使用
1 | > 'user: Alan'.match(/user: (\w+)/) |
我們可以幫這些群組命名,讓他更符合語意
1 | > result = 'fullName: Alan Hsu'.match(/fullName: (?<firstName>\w+) (?<lastName>\w+)/) |
non-capturing group
如果捕獲的文字不重要,可以在前面加上 ?:
1 | > '192.168.0.1'.match /(\d{1,3}\.){3}\d{1,3}/ |
backreference
我們可以用 \1
\2
等等來代表已經捕獲的文字
1 | > ('2a2b').match(/(\d+)a\1b/) |
Assertions
wiki:
斷言是一種放在程式中的一階邏輯 ( 例如一個結果為 true 或 false 的判斷式 ) ,當程式執行到斷言的位置時就會執行判斷,若結果為 true 則繼續執行,結果為 false 則中止執行。
前面提到的 ^
$
也都是 assertions 的一種,常見的還有文字邊界 \b
跟 lookaround
文字邊界顧名思義就是會確定所在位置是不是文字的邊界
1 | > 'difference between Javascript and Java.'.sub(/Java/, 'Ruby') |
Lookaround
這部份是比較進階的用法
1 | ?= is for positive look ahead |
先來看 lookahead , 意思是「往前看」,語法為:
Positive lookahead : X(?=Y)
Negative lookahead : X(?!Y)
解釋為: 我要找 X 而其後方必須/不可為 Y ;而其中 X 和 Y 都可以是一個合法的表達式。
比方說 /a(?=b)/
會 match 到 “ab” 裡面的 “a”,但不會 match “ac” 裡面的 a,要注意的是雖然有用括號刮起來,但像是 non-capture group 一樣,他們並不會被放到群組裡面
1 | > 'abc'.match(/b(?=c)/) |
lookahead 跟 non-capturing group 有點像,但 lookahead match 到的部分不包括括號裡面的東西
1 | > 'abc'.match(/a(?=b)/) |
Examples
1 | > group = "You: are not coool".match(%r{(^.*):(.*)}) |
1 | > target.match(%r{(application|assessment)/(\w+)}) |
找到 html tag 裡面的雙引號:
[^<]*>
代表最後是 >
然後前面是除了 <
之外任意東西的 n 個
"(?=[^<]*>)
就是找到雙引號,他的後面需要有 >
,然後這個 >
的前面到雙引號之間不能有 <
1 | > '<tag>this is a double quote " </tag>'.match(/"(?=[^<]*>)/) |
Ruby 相關
ruby 的 %r
%r
裡面的東西會自己轉變成 regular expression(class 是 Regexp)
好處是不用去做 escape
1 | %r{/home/user} |
match
一般常見的用法:
mtach 的結果會回傳 MatchData 物件
1 | > string = '{fontsize: 54}Biigger text{fontsize}' |
如果是用 block,裡面被 yield 出來的參數也是同樣的 MatchData 物件
1 | string.match(/\{fontsize: *(\d*)\}(.*?)\{fontsize\}/) do |match| |
gsub
可以在 gsub 裡面使用 regular expression 應該大家都知道,但如果想要比較複雜的操作,在 gsub 的 block 裡面 yield 的物件是 String 物件,可以用 RegExp.last_match
拿到 MatchData 物件
1 | > string.gsub(/\{fontsize: *(\d*)\}(.*?)\{fontsize\}/) do |s| |
=~
如果只是想看某段字串是否符合 regular expression 可以用這個 operator
他的回傳值是符合的地方的 index
1 | > 'test123' =~ /123/ |
scan
會把符合 regular expression 的部分都放到 array
如果要用 group 的 regular expression 則會變成 nested array
1 | > '123test123'.scan /123/ |
參考資料
五倍紅寶石的介紹
Stackoverflow
Stackoverflow: Difference between ?:, ?! and ?=