2009年12月19日 星期六

正規表示式 (regular expression)

* 指前所綁住的字元或字元集合,出現 0 次或 0 次以上。
\+* 作用相同,但不包括出現 0 次。
\= 指前所綁住的字元恰好出現 0 或 1 次。
\| 這是多選,就是 or 的意思,被 \| 隔開的 pattern,任一個符合的話就算符合。

\+, \=, \| 會加上一個 \,是因該字元在 Vim 就具有特殊意義,在一般的 regexp 中是 +, ?, | 就可以了,只是提醒您一下,以免搞混了! 記住 \+ 是不可數的!用辭不是很精確,只是幫助您記憶啦!

[實例] dg*
指 * 前所綁住的字元 g 出現 0 次或 0 次以上。也就是說 d(出現 0 次),dg, dgggg, dgggggggg 都是符合這個 pattern。如果您下尋找指令 /dg*,那符合這個 pattern 的字串都會被找出來。如果用在代換就要非常小心了,像 extended 中的 d 也是會被置換掉的。例如您下 :%s/dg*/test/g 的話,那 extended 這個字會換成 extentestetest。

shell 中使用的通用字元為 pattern matching notation 和 regexp 是不同的意思。dg* 在 shell 中是解為以 dg 開頭的任意字串,這就不包括 d 在內了,也就是說在 shell 中,* 是代表任一字元或字串,這在初接觸的朋友很容易就搞混,請千萬小心。

[[實例]] dg\+
dg, dgg, dgggggg 皆符合,但 d 則不符合。如果是 dg\= 的話,就只有 d、dg 這兩個符合了。

[[實例]] :%s/The\|All/test/g
全文中只要是 The 或 All 都會被替換成 test。注意,如果文中有 There 也是會被替換成 testre!要如何避免這種情形呢?下面會另述及限定使用法。

[[實例]] /123-\=4567
這樣會找出,123-4567 及 1234567。當然 123-456789 也是會被找出來。

[...] 字元集合,表示中括號中所有字元中的其中一個。
[^..] 這是上述 [...] 的補集,表非中括號內字元的其中一個。
. 除換行字元外的任一單一字元。指本身,非指前所綁之字元。就好像 shell 中的 ? 一樣。如果要指定真正的英文句點,要用 \來 escape,就是說 \. 這時的 . 是代表真正句點,而不是 regexp 中的特殊意義。其他如 \* 亦同。

[[實例]]
[Aa]
A 或 a 其中的一個。
[12345]
12345 其中的一個數目字。可用 [1-5] 來表示。連續性的數目字或字元可用 -來隔開,寫出頭尾來代表就可以了。[0-9] 就表 0 到 9 的數目字,[a-d] 就代表 abcd 四個英文字母

[[實例]] W[0-9]*\.cc
這個例子是說以 W 開頭,後接 0-9 其中一個或多個數目字或不接什麼,然後是一個句點,最後是 cc。所以 W.cc,W1.cc,W2.cc,W345.cc,W8976543287.cc 皆符合。如果要表示 W 及 .cc 間夾一個以上的數目字,要寫成 W[0-9][0-9]*\.cc。

[[實例]] .*
這代表任意字元或字串,或什麼都沒有,腦筋急轉彎,對照前面的定義想一下。當然這是不包括換行字元的。

[[實例]]
[^M] 表除 M 以外的任意字元。
[^Tt] 表 T 及 t 以外的任意字元。
[^0-9] 表非數目字之字元。
[^a-zA-Z] 表非英文字母之字元。

注意,^ 要在中括號內,且在最開頭的地方,否則另有含意。

^ 匹配行首,指其後綁住的字串,出現在行首才符合。
$ 匹配行尾,指其前綁住的字串,出現在行尾才符合。含換行字元。

不是在行首的 ^ 指的是 ^ 這個字元。不是在行尾的 $ 是指 $ 本身這個字元。

[[實例]] /^What
這樣只有在行首的 What 才會被找出來。注意! Whatever, What's 也是會被找出來。如果是 /What$ 則是在行尾的 What 才會被找出來。

[[實例]] ^$
這是什麼東東?行首也是行尾的行。ㄚ,就是空白行嘛!當然也不能說這個行是沒有什麼東東啦!空白行至少也是會有個換行字元。在後面會詳述如何消除全文的空白行。

\(...\) 記憶 pattern,可由 \1, \2...\9 來叫出。

[[實例]] :%s/\([a-z]\)\1/test/g
這樣 aa, bb, cc, dd,...zz 都會被 test 替換掉。這和 :%s/[a-z][a-z]/test/g 是不一樣的意思,後者會把 aa, ab, ac...ba, bb, bc...zz 都換成 test。也就是說 \(...\) 由 \1 叫出時會有對稱性的配對出現。

[[實例]] :%s/\(.\)\(.\)r\2\1/test/g
會將中間為 r,前有二個任一字元,後有兩個具對稱性的字元所組成的字串替換成 test。\2 是呼叫第二組 \(.\),而 \1 是呼叫第一組 \(.\)。例如:12r21,cfrfc,7grg7 等都會被替換成 test。

\< 匹配字(word)首。所謂 word 包括文數字及底線。
\> 匹配字尾。這就是前所提及的限定用法,被 \<,或 \>括住的 pattern 就會被限制住,使 regexp 不能再向右(左)擴充解釋。

在 Vim 中 \b 是表示 <BS>即 Backspace 鍵。

[[實例]] :%s/\<abbbc\>/test/g
這樣只有 abbbc 才會被替換成 test。如果沒有這樣限定,:%s/abbbc/test/g,那 deabbbcly 中的 ``abbbc'' 亦會被替換成 test。所以前面 :%s/The\<All/test/g 可換成 :%s/\<The\>\|\<All\>/test/g 這樣一來,There 就不會被替換成 testre 了!

[[實例]] :%s/\<abbbc/test/g 這樣的話,只要是以 abbbc 為首的字(word),其中的 abbbc 的部份都會被 test 所替換。注意!是指字首,而不是指行首。所以 abbbc,abbbcerd ,abbbckijuds 都符合。

\{n,m} 指前所綁住的字元或字元集合最少出現 n 次,最多出現 m 次。

這在一般的 regexp 表示成 \{n,m\}。以下會舉四種不同的例子:

[[實例]] \{最小值,最大值}
[0-9]\{3,4} 匹配至少三位數,但不可多於四位數的數目字。如:
123
12
1
123456
1234567
12345678
1234
12345

如果下 :%s/[0-9]\{3,4}/test/g 的話,那 1,12 這兩組不會被替換,因為不滿 3 位數。而 12345,則會換成 test5。123456,則會換成 test56。12345678 ,則會換成 testtest。1234567 也是會換成 testtest。123,1234 這兩組則會被替換成 test。您可以親自操作一次就知道怎麼一回事了。操作時最後加 gc 來 confirm,這樣您會更瞭解實際替換的內容。ㄟ,別忘了 u 可以回複您的編輯動作。

[[實例]] \{數目字}
xy\{20} 表示 x 後接 20 個 y。 e[x-z]\{4} 表示 e 後接有四個字元,是 x,y,z 的其中一個的組合。如:exxxx, exyyz, ezzyz, exyzz 皆符合。

[[實例]] \{最小值,}
xy\{2,} 表 x 後接至少二個的 y。相當於 xyyy* 或 xyy\+。

[[實例]] \{,最大值}
xy\{,4} 表 x 後接至多四個或更少的 y (可能沒有)。因此 x, xy, xyy ,xyyy, xyyyy 皆符合。

替代變數
代表置換時合於 patern 的字元或字串。

[實例] :%s/\d\d\d\d\d\d\d\d\d\>/ID:&/g
這樣全文中的身份證字號前就會加上 ID: 字樣,也就是說 T123456789 會被換成 ID:T123456789。還記得嗎? \d 就是 [0-9]\u 代表大寫的英文字母。加個 \>是防止 T12345678999 也被換掉。當然前面再加個 \<更保險。ID: 字樣您用中文也行!另一個好用的例子是電話號碼前加上 TeL:,就請您自行練習了!

[實例] 將檔案 3 至 7 行的資料向右移 2 個空白
:3,7s/.*/ &/
但這樣連空白行也是會插入空白字元,較高明的做法是:
:3,7s/.\+/ &/
這樣空白行就不會去動它了!想通了 .*.\+ 的意思了嗎?往前翻一下 . * \+ 的定義。

[實例] 將檔案 3 至 7 行的資料向左移 2 個空白
:3,7s/^ //
就是刪去行首的二個空白啦!

[實例] 將全文的 Edward 這個單字,前後加上中括號
:%s/\<Edward\>/[&]/g

[實例] 將全文的 Edward 這個單字,改成大寫的。
:%s/\<Edward\>/\U&/g

\U 不是代表非大寫字母嗎?喔!您搞錯位置了。\U 在 pattern 的位置的時候是指非大寫字母的樣式,即 [^A-Z],但如果是在置換字串位置的時候是指將其後的字串通通改成大寫。與其相對的是 \L,會將其後的字串改為小寫。詳細請 :h sub-replace-special。

[實例] 將全文每行最後加上 <BR>這個 HTML tag。
:%s/.*/SPMamp;<BR>/g

[2010.01.23 補充]
[實例] 把所有的字串改為1000的倍數
:%s/\d\{3\}$/000/g
:g/[0-9][0-9][0-9]$/s//000/g
感覺起來,g/應該就是把文章內容依後面的patter尋找到的資料存到暫存器中,並且,把暫存器的內容再做之後的動作(/s//000/g)。

[2017.03.03 補充]
把英文單字只保留頭尾,中間用底線取代
例:
good
help
student
school
會變成
g _ _ d
h _ _ p
s _ _ _ _ _ t
s _ _ _ _ l
參考
%s/\<\@<!.\>\@!/_/gc
參考資料:
正規表示式的應用
規則表示式的運用

沒有留言: