在我們之前講了模式選擇,和一般編輯和刪除的方式,這些可以算是最基本的 vim 操作。但是對於一份龐大的文件來說,我們經常只是想要修改其中的一小部分而已,這個時候我們就會需要 search (搜尋)的功能,來幫我們在龐大的文件找到我們想要的那一小部分。
在 vim 裡面,最簡單的 search 莫過於在指令模式下按「 / 」然後跟上你想要找的字串的方式。例如:
/I love vim
不過這種方式是從游標所在位置由上往下找,也就是說從檔案的開頭開始往檔案結束的方向找起。如果你想要從下往上(也就是反方向)找的話就可用「 ? 」,比方:
?I love vim
如果你習慣用 firefox的話,或是相類似的軟體,在 windows 上一些應用軟體在做了搜尋找到第一個比對到的字串之後,想要找下一個會按<F3>,但是在 vim 裡面的話,則是用「 n 」。如果要往回找的話,就是取大寫的「 N 」。其實如果你想直接用「 / 」後面不跟上任何東西也是可以的。
在 vim 裡面的命令(以冒號開頭的),或是在用這些搜尋指令「 / 」或「 ? 」時,都是可以按上下鍵來選擇之前或之後輸入過的字串。
比方說我們之前已經搜尋過 firefox 和 linux 這兩個字串,當按「 / 」然後按上鍵就會出現
/linux
若再按一次上鍵,則會出現
/firefox
但是通常你在搜尋一份文件的時候,你明明看到某個字,可是要搜尋還要打出完整的東西,比方說你看個程式,程式裡面寫了一個 function name 叫 BF_algo_fromsrc_pathcost。難道就真的要打一遍?老天﹗而且 vim 的比對預設是有大小寫差異耶!這裡有個小東西可以幫你的忙:
「 * 」
也就是說你只要把游標移到這個 BF_algo_fromsrc_pathcost 字串的上面按一下「 * 」, vim 就會幫你尋找出這個字串的邊界,並且搜尋該字串。不過 vim 在這裡搜尋邊界是根據空白或是像「 { 」、「 ( 」這些符號等來決定,所以對於中文來說,這個功能可能就不是那麼好用。但是別忘了我們還有 select(選取)模式。我們可以把游標移到我們想要搜尋的字串的開頭,然後按下小寫「 v 」然後移動游標到欲尋找字串的結尾後再按下「 y 」。「 y 」在 vim 中表示的是 yank。在這裡我們暫時不多說明和 yank 有關的指令或行為,這裡我們只要先把 yank 想成是把選擇的區域複製一份到 vim 的暫存區(register)裡面就好了。複製好之後,我們就可以繼續作搜尋,就是輸入搜尋指令「 / 」或「 ? 」之後,按< Ctrl-R >然後跟上數字鍵 0 ,就會出現剛剛複製進入暫存區( register)的字串。指令如下:
/<Ctrl-R>0<CR>
在 vim 裡面的暫存區(register)中,register 0 是作刪除或是 yank 動作時,預設使用的暫存器。目前我們也只要記到這樣就好了。
不過在前面可以發現,如果我們用「 * 」作搜尋的時候,字串的前後會被「 /< /> 」夾著。舉例來說,如果你找的是 /<link/>,那麼, min_links 就不會符合,同樣,linknode也不會符合。這也就是說「 /< 」表示的是一個字的頭,而 「 /> 」代表的是一個字的尾,當你寫/<link的時候,表示你要找的字,是以link開頭的,同理,link/>就是以link結尾的字,所以用兩個夾起來,就代表要字串的的確確是所要的那個字。
而既然有了 「*」往下找的,就有往回找的,按鍵是「 # 」 ,你懶得記那麼多的話,就用 「*」 然後搭配 「 N 」 吧﹗
到這裡,你可能會想,如果我不想打那麼多字,可是我想要的又不要完全是一樣的,怎麼辦? vim 也有這樣的東西,就是在你打「*」之前,先加一個 「g」,也就是
g*
這樣 vim 在做搜尋的時候,就不會包含 /< /> 這兩個東西在前後了。同樣「 # 」的使用也可以變成「 g# 」
以上部分可以說 vim 上的基本搜尋功能。但是有時候你想要找的東西其實有部分是有規則可循的,在這個時候就會需要一點點的 regular expression(正規表示式,我個人是不太喜歡這個翻譯,因為這樣 regular 和 formal 就不分了) 。不過在 un*ix 中的正規表示法和一般學 formal language 的符號表示不同。不過這些討論我們就先擱下,還是先來討論什麼叫做部分有規則可循的字串。
舉例來說,我今天想要找四逆湯、四逆散或甚至是某某四逆丸。在 vim 裡面因為預設會有 search highlight,也就是說搜尋到的字串會以高亮度的顏色表示出來。好讓我們能一眼就看出來哪些部分有被比對出來。所以在這個例子裡面,如果我們只針對上面三種狀況各做一次搜尋的話,就比較麻煩。我們就沒有辦法把這些我們想要的字串都一次比對出來。所以在 vim 裡面我們可以這樣用:
/四逆[湯散丸]
中括號「 [ ] 」裡面夾的就是可以選擇的字元,不過這裡的字元也是根據語言編碼有關。換句話說,如果我們今天要找的是英文文件,想要搜尋的 lemon tree、lemon soup、lemon juice 的話,就不能這樣打:
/lemon[tree soup juice]
這是因為在英文中 t、r、e、s、o… 等這些都是被當作一個單一的字元來處理,所以在英文文件中我們不能這樣做。這時候我們就必須用「 /| 」這個「或(or)」運算。套剛剛的英文字串就可以打成:
/lemon tree/|lemon soup/|lemon juice
這就表示我們要找的是 lemon tree 或 lemon soup 或 lemon juice。不過要注意的是在中括號裡面只是表示只從中選擇一個字元。所以拿剛剛中文的例子來說,文件中出現四逆湯、四逆散、四逆丸的話,都是會比對出來的。但如果只有四逆,後面卻不是跟上湯散丸的話就不會被找出來。
不過有時候你想要搜尋的東西會是像這樣 1234- 1234-5 1234-56 1234-567 1234-789 1234-2399 1234349898。
我們大概可以分析一下,這個字串大約是以1234開頭,然後跟上或不跟上「-」之後,再接上一堆數字。先把指令給大家:
/1234[-]/{,1}[0-9]/{1,}
或是:
/1234-[0-9]/{1,}/|1234[0-9]*
我們可以先看第一個方法,在第一個方法裡面用了兩個特別的東西:/{1,} 和 /{,1}。在前面我們剛剛說中括號後面如果不跟上任何東西就表示中括號裡面的某一字元要出現一次。而現在/{n,m}就是用來表示可以對中括號裡面的字元挑選幾次。(選幾次就會出現幾個字元)
像/{1,} 表示至少要出現一次(至少選一次),而/{,1}表示最多出現一次(最多選一次,不選的話就代表中括號裡面的字元都不出現)。當然我們也可以寫成/{3,9},這就表示可以在中括號裡面的字元挑選三到九次,也就是會出現三到九個中括號裡面出現的字元。
而在第二個方法裡面,則在中括號後面緊跟著一個「 * 」,這其實代表/{0,},也就是 0 個到任意個都可以的。而中括號裡面我們用了「0-9」這就表示 0123456789 都可以。同樣對於字母我們也可以用「a-z」表示所有小寫英文字母和「A-Z」用來表示所有的大寫字母。
儘管如此,我們可能還是不滿足。舉例來說,我可不可以只找以「傷寒」開頭的字串或是以「主之。」結尾的字串?
假如我們只想找以「傷寒」開頭的字串,可以這樣輸入搜尋命令:
/^傷寒
「 ^ 」開頭就表示要找的東西是一行的開頭。
但是有時候為了排版需要,在「傷寒」前面可能會出現一些空白或是 tab ,如果我們只是用上面的指令就不夠。所以配合我們剛剛的中括號帶次數的方式就可以做到:
/^[/t <Space>]*傷寒
<Space>是為了網頁展示可能看不到所以用<Space>來表示空白。而「/t」則是 vim 中用來表示 tab ,所以上面的命令就代表一行的開頭有任意個空白或是 tab 的混合,而其後跟上「傷寒」兩個字。
那結尾呢?還記得我們怎麼把游標移到行尾嗎?就是用「$」這個符號。所以要找以「主之。」結尾的字串就可以用:
/主之。$
不過你可能還是不滿意,比方說 vim 可不可以幫我找出忘了在句末補上句號的地方?可以!我們先看指令:
/.*[^。]$
或是不要看到整句:
/[^。]$
中括號裡面的字元 X 如果前面出現「 ^ 」就表示除了 X 以外的字元都算是想要比對的字元。所以上面第一個的指令就表示搜尋句末沒有句號的行(這一行至少要有一個字元,因為用了[^。]$,而不是[^。]*$)。第二個指令比較不一樣是只搜尋句末不是以句點結束的字元。其實看哪個指令對你來說比較「順眼」就用哪個。
有關這些特殊符號還有許多更 powerful 的功能,你可以在 vim 的指令模式下打 :h pattern 看更詳細的資料。或用 :h regular 看有關 regular expression 的詳細說明。
現在,你已經可以用 vim 編輯、刪除,也可以做搜尋了,基本的 vim 操作到這裡已經可以說是差不多了。
下一次,我們會討論 vim 裡面的游標移動,以及搭配 ctags 的一些功能,你會對 vim 認識更多也會更喜歡它。
-------------------------------------------------
总结:
搜索可以由以下字符开始: / ? * g*
另外还有一种非常有用的搜索方式: "v" -> "利用hjkl"选择用搜索的内容 -> "y" ->"/CTRL+R0"回车(注意0是数字零)。
例子归纳
搜寻命令 命令理解 /四逆[湯散丸] 找四逆湯、四逆散或甚至是某某四逆丸。 /lemon tree/|lemon soup/|lemon juice 搜尋 lemon tree、lemon soup、lemon juice /1234[-]/{,1}[0-9]/{1,} 结果像這樣 1234- 1234-5 1234-567 1234-789 1234-2399 12343498。 /^傷寒
/^[/t <Space>]*傷寒找以「傷寒」開頭的字串。 「 ^ 」開頭就表示要找的東西是一行的開頭。 /主之。$ 找以「主之。」結尾的字串 /.*[^。]$
/[^。]$在句末没有句號的地方. 第一个命令会高亮整句话。 第二个命令只高亮最后两个非句号结尾的字符。
/*中括號裡面的字元 X 如果前面出現「 ^ 」就表示除了 X 以外的字元都算是想要比對的字元。*/