正規表示式(Regular
expression, RE)--規則篇:
正規表示式是個強大的工具,定義了明確的定串比對規則,來幫助使用者把梳字串,在很多程式都有支援,如Linux
shell 的grep,
awk, sed,vi(m)的搜尋取代功能等,C語言的scanf其實也有支援,可惜正規表示法規則甚多,一時之間難以記全。
這篇主要記錄正規表示法的規則,整理自“Practical
Programming in Tcl and Tk”第十一章,給自己做個參考:
- 一般字元與跳脫字元:
如果要比對字串,最簡單的規則就是:把字元寫出來,當寫:
a,就會比對出a,夠簡單吧。
但這樣就沒啥變化了,如果我要比對a,
aa
或aaaaa...aa呢?如果全部的東西都是直接比對,就少了這種彈性,所以正規表示式定義下例幾個字元為跳脫字元,平時做為特殊處理之用,要比對他們需要特殊規則,包括:
.*+?()|[]^$\
在比對中寫下這些符號的功能之後會一一介紹到,如果是要比對這些符號,就用backslash
\ 跳脫。
例如要比對\,就需要寫\\而非只有\
如果我們要寫一個讓使用者輸入y/n的程式,使用者可能會填y/Y/n/N裡面一個,因此就有了字組的概念,字組利用跳脫字元[],讓某一個位置有多重選項:
[yY],則y或Y都可以match
在[]裡面可以使用“[x-y]”的規則,來比對某區間的字,例如:
[0-9]比對所有數字,
[a-z]比對小寫的英文。
這裡要注意的是,隨著語系的選擇,[x-y]會有不同的範圍,不過一般都是以LANG=C的情況下去設計,也就是0-9A-Za-z的排序。
子規則1:反向用[^],例如我要比對“不是<”,就用[^<],如果是[^a-z],就會排除所有小寫英文。
子規則2:比對跳脫字元除了]需要在[]要排第一位、\仍要跳脫外,其他在[]就不用跳脫,直接寫就可以
例如下行會比對出所有的跳脫字符:
[][.*+?()|\\]
另外則是{}這個直接寫就是直接match,但加上\{,\}後則是跳脫為特殊意思。
如果像上述,我們要比對a,aa或aaaaaaa...aa,或者我們只要使用者輸入「o某字z」,他要orz,
otz,omz 我才不管呢?
首先使用的是「.」,這是RE中的「任意一個字元」,跟shell的不太一樣,這要小心。
所以上面的比對,我們可以寫 o.z
那針對不同的數量,我們就需要數量字符:*+?
以及{m,n}
字符 |
代表數量 |
* |
0~∞次 |
+ |
1~∞次 |
? |
0~1次 |
{m,n} |
m~n次,其中n可以不填,表示∞ |
所以說,上面的比對可以用 a+
完成。
重複的可以用()包起來,[]裡面的重複也OK,將數量字符直接加在下括號的後面即可。
比較重要的是 * 這個符號用起來要小心,這個比對表示任意字符重複0~n次,有時會造成不預期的結果。這就牽扯到正規表示法的比對規則了,如果一個match在字串中有多個相符的比對時,其規則如下:
- 各相符比對,最接近開頭者優先:
- 如果是一樣的開頭,預設最長者優先,這個可以改換為nongreedy,即較短者優先。
所以如果用 [a-z]*
去比對 123abc,一般可能會預期取出abc,但不對,在123前的開頭的空字串才是優先match的對象,一般都建議用+強制一定要有match來避免這樣的狀況。
字集為一些設定好的字元組合,寫法為:[:identifier:]
整理可選用的identifier如下:
這部分兩個定位就是^和$
=w=
1 特殊符號類:
表示法: |
比對: |
等同符號表示: |
\a |
Alert
character.
|
|
\b |
Backspace
character.
|
\u0008 |
\e |
Escape
character(ESC) |
\u001B |
\n |
Newline
|
\u000A |
\B |
Backslash |
\\
|
\0 |
NULL |
\u0000
|
\r |
Carriage
return |
\u000D |
\t |
Horizontal
tab |
\u0009 |
\f |
Form
feed
|
\u000C |
\v |
Vertical
tab |
\u000B |
\cx |
Control-x
|
|
2 字集類:
表示法 |
比對 |
等同表示法 |
\d,
\D |
數字,
非數字 |
[[:digit:]]
,[^[:digit:]]
|
\s,
\S |
空白類字符,非空白類字符 |
[[:space:]]
, [^[:space:]] |
\w,
\W |
字母+數字+底線,
非以上組合 |
[[:alnum:]_]
, [^[:alnum:]_]
|
3
定位字符類:
表示法 |
比對: |
\A,
\Z |
字串開頭,
字串結尾
*還沒找到相對應的範例 |
\m,
\M |
單字開頭,
單字結尾
[[:<:]]
[[:>:]] |
\y,
\Y |
單字開頭或單字結尾,
非\y
*還沒找到相對應的範例 |
4
數字指定類:
表示法: |
比對: |
\unnnn,
\Unnnnnnnn |
16-bits,
32-bits Unicode character code.
|
\xhh
Consumes all hex digits after \x.
|
An
8-bit hexadecimal character code. |
\x,
\xy, \xyz |
xyz為數字,這個可以是back
reference,或者是8-bit
octal character code |
之前提過RE的規則是愈長愈好,設定Nongreedy即是變成愈短愈好,使用的是? 加在第三項的數量(quantifiers)比對後面,比如說我們要match整個輸入中的第一行,如果寫:
.+\n 這樣會match整個輸入,直到最後一個\n符號,其中包括很多很多換行符號。
.+?\n 設定+為nongreedy,則遇到第一個\n就會結束比對
或者我們要抓<title></title>中間的文字:
所有的數量比對加?在後面都會變成non-greedy,??代表0次優先,{1,3}?則會以1,2,3的順序去match。
這個規則比較重在要把內容存到暫存裡的狀況,一般的狀況下似乎不太用到。
在正規表示式中除了直接match的結果外,還可以用小括號()來記錄子比對的結果:
例如<title>(.*?)</title>,除了match整段文字外,可以取出兩個tag中間的文字內容。
要取出這個這個比對結果,可以用Back
reference,使用的方式是\x,x為數字,依數字對應到各子比對結果,子比對結果的排序為“外到內,左到右”,如下所示:
(一(二))(三),注意(一)是包括自己跟(二)的內容。
另一方面,如果我們只想要比對,卻懶得管一些比對的內容呢?這就需要look
ahead,我想到最需要誕個功能的地方是rename,我要改掉所有txt檔,卻只要取出檔名的部分,這時候就需要look
ahead'
例如我要取出副檔名為.txt的文件名稱:
^.*\.txt$,可以,但記錄下來的是:lalala.txt,.txt是不需要記錄的
positive
look ahead:^.*(?=\.txt)$,記錄的就只有lalala
negative
look ahead:^.*(?!\.txt)$,這反過來,比對出非.txt結尾的檔案名稱。
還有一個是(?:pattern),同樣也是不記錄pattern中的內容,但跟look-ahead的差異點在哪我就不是很確定了。
其實主要的功能就上述這些,我覺得主要會用到的大概只到第6點,我在vim會用到第8點,我提的那本書還有兩個功能,一個是collating
element [.identifier.],只是書裡沒說得很清楚,我也還沒試出幹什麼用的…;另一個是Equivalent
class [=char=],只有比對Unicode例如[=o=]可以比對 o,
ō,
ŏ,但平常好像也用不到。
這篇主要整理規則,之後會寫一篇有關應用方面的實例與方法。
如果是有關C語言中,有關scanf的部分,可以參考scanf的整理,一些基本的正規表示法語法在scanf中其實是有支援的,請見:
(worship)
回覆刪除=_=,QCL你強者給點有建設性的回覆啦…
刪除今天我跟一個資工系的吃飯,他就說到,有個同學就說…那個html有個QCL同學有整理…他叫…他叫…反正他超強的啦
是說RegExp我覺得可以搭上state machine來說明會更好理解 :-)
刪除以前我也看不太懂,直到學了自動機,RegExp就是用來表述一個語言的。
html是指 http://goo.gl/ib1bX 嗎? 那是抄來的啦wwww 而且還沒有整理完就放棄了OTZ
啊…stae machine是指有限狀態機嗎orz
刪除不知道這跟regexp有什麼關係@_@
寫了一點小小的關聯放在我的網誌上,可以參考參考XD
刪除http://blog.qcl.tw/2012/08/blog-post.html
我…我才不是神人呢…我只是個花盆…
刪除是說看起來你的網誌也是草創中
敝人從國中畢業開始寫網誌,只是把以前的東西都先收起來整理中而已XD
刪除(worship)
回覆刪除我想看這個網頁的教學 http://docs.python.org/library/re.html,希望葉闆大事能寫一篇 XD
回覆刪除打錯,是大師
刪除我最近也在學python,只是手感還沒練出來orz
刪除