2010年1月23日 星期六

直接透過vim去取代文件內容

在之前,我要取代文件的內容,都是使用外部指令grep,來完成!

雖然,這樣也可以達成,不過,其實透過vim的內建功能就可以達到這一個功能啦~

Q: 選取特定幾行,執行取代的動作
A: 在之前我就要去算由第n行到第m行要執行取代的動作,那麼我就直接使用以下的動作
: n,ms/{pattern}/{string}/gc
而這裡還有另一個方法:
在你想置換部分的開頭按下大寫「V」然後用移動游標(我們之前講的 vim 移動游標的方法在選取模式下都可以用)到你想要的位置之後按「:」就會跳到輸入指令的狀態,如下圖最下面那行看到的一樣:


或者是如果你很確定要從現在游標所在行之後的所有行都置換,可以下成
:.,$s/vim/VIM/g
冒號一開頭的那一小點「 . 」就代表游標現在所在行,「$」則用來表示最後一行。

可以找到系統的相關說明
:h :s

假如我現在有一筆人名、電話的資料,由於是隨手記的,上面自然就是沒有排序過。那沒排序過對於想要在上面找資料的人就很麻煩。萬一人名記不太清楚,電話號碼也記得七七八八,雖然說有 vim 方便的 search 功能,但總是感覺不足。(當然這只是假設情況,因為實際上可能大家都已經建立某種方便搜尋的資料庫了)

我們先假設人名、電話的對應長成這樣:

趙大明  1235478982
錢小名 1223450012
王孫李 5938123812
周渚衛 1384914191
沈以情 2345934981


那我可不可以讓它變成這樣子?

1235478982   趙大明
1223450012 錢小名
5938123812 王孫李
1384914191 周渚衛
2345934981 沈以情


我可以下這樣的指令完成這個工作:

:%s/\([0-9]\{1,\}\)\([^0-9]\{1,\}\)/\2 \1/g

前面部分\([^0-9]\{1,\}\)\( \)括起來的,表示這是一個的單元,之後就可以依照它出現的順序而使用 \1, \2,… ,來代表它。所以我們可以看到[^0-9]就表示非數字的部分,後面的\{1,\}如果你還記得的話,就是代表出現至少一次。可以用「\+」來代表至少出現一次。你可以用 :h /multi 看到更多這些替代符號。

所以把 pattern用\( \)分開之後,我們就可以用 \1,\2 來把他們交換位置。

最短符合

之前提到的 "\{n,m}" 會盡可能的找最長符合字串。若是要找最短符合字串,要改用 "\{-n,m}" 。

譬如說:

/ab\{-1,3}

會與字串 "abbb" 中的 "ab" 符合。因為要求的是最短符合字串,所以只會比對到1個"b"。如果要強制它能比對到比下限多一些的話,還得要多加點東西。

同樣的規則也可以套用到去掉 "n" 或 "m" 的情形。甚至可以同時不寫 "m" 和 "n" ,只留下 "\{-}" 。這個指令的意思是符合零次以上,愈短愈好…所以一定會比對到符合零次。這些規則可以組合使用,譬如:

/a.\{-}b

會對 "axbxb" 中的 "axb" 符合。如果改用:

/a.*b

就會試著找最長符合字串,所以就會符合到整個 "axbxb" 。

[2010.02.27 補充]
把完整的目錄名稱,只留下檔案名稱
例如內容為
d:\work\project\main.c
d:\work\project\make.dxc
d:\work\main.h
可以使用
:%s/.*\\\(\w\+\.\w\+$\)/\1/g
尋找字串,必需滿足以下二個條件
1. 先說明\w\+\.\w\+$的部分,是以尋找符號.前,至少有一個非特殊字元且後面也是至少有一個特殊字元的字串,並且後面就是字尾啦,這樣會找到main.c、make.dxc與main.h;
而用\(...\)把它包起來,就是要把它當作pattern,等一下取代則可以使用。
2. .*\\則表示找到在\前面有任意字元的字串
當內容中,有符合以上的字串,就處理\1這一個動作,也就是把符合這一個字串的部分,整個替換成第一個pattern

現在又想到一個問題,Makefile是沒有副檔名的,所以沒有辦法使用「任意字串.任意字串」當作條件去尋找啦。
還好又想到正規表示式是貪婪的,如何在同一行符合的字串長度有兩個,則他會選則最長的那一個。例如:
d:\work\project\main.c
符合.*\\條件有d:\與d:\work\與d:\work\project\
則,它會選擇最長的部分也就是d:\work\project\
因此,在這裡使用
:%s/.*\\\(.*\)/file name is \1 end/g
就可以取出檔名,並且在前面加入一些字串和後面加一些字串

[Add-20100307-start]
全域性的指令
:[range]g/pattern/[cmd]
cmd 是 ed 可用的指令,預設是 p(print),您可查一下 man ed,就可以知道有什麼指令可用。
需注意的是,d 是行刪除指令,凡含 pattern 的整行都會被刪掉,而且 range 不指定的話,預設是全篇文章,因為 g 就是代表 globe。

Q: 刪除空白行
:g/^$/d
有些書中寫成:%s/^$//g可以刪除空白行,這是錯誤的,因為 :s 這個指令只更動一行裡的內容物,但不會做刪除一行的動作。
[End-20100307-start]

[2010.04.05 補充]
Q: Delete all lines containing which didn’t contain string
A: :v/string/d

Q: want to match all non-printable characters and delete them
A: /\p\@!.
上列後面那一個小數點的意思是說滿足這一個條件的是有幾個字元,因為,上述只有一個小數點,所以是一個字元

Q: 找到不滿足的字串(不含有"String")
A: /\(String\)\@!

Q: 重覆a這一個字元3次
A: /a\{3}

Q: 重覆abc這一個字串元5次
A: \(abc\){5}

[2010.09.22 補充]
Q: 移除每一行最後面多餘的空白
A: %s/ \+$//gc

[2012.02.20 補充]
Q: 尋找最短符合字串,如何把以下內容前面縮寫的部分移到最後,並且加括號
API Application Program Interface
BLDK Boot Loader Development Kit

A: %s/\(^[A-Z]\{-}\) \(.*$\)/\2 \(\1\)/gc

參考資料:
vim 的取代置換功能「s」
vim 的 search 搜尋功能
規則表示式的運用
100 Vim commands every programmer should know
help pattern-multi-items

沒有留言: