標籤:

理解使用 vim 中的正則表達式

理解使用 vim 中的正則表達式

vim 存在 4 種模式,並且在不同場景有不同的使用方式,本文做一些簡單小結。

4 種模式概覽

以下摘自 vim 文檔:

after: v m M V matches ~ magic nomagic $ $ $ $ matches end-of-line . . . . matches any character * * * * any number of the previous atom () () () () grouping into an atom | | | | separating alternatives a a a a alphabetic character \ \ \ \ literal backslash . . . . literal dot { { { { literal { a a a a literal a

並不需要記住這些,因為你可以保持只使用其中一種模式,需要幫助的時候 :h magic 查看即可。

默認模式以及改變模式

除非你設置 magic 選項(不推薦),vim 在所有情況都默認使用 magic 模式,也就是 m 進行匹配,假如你搜索 any number 兩個字元串,你需要這麼敲:

/(any|number)

因為 ( 和 | 在默認 magic 模式是沒有特殊含義的,如果你使用 v 模式就可以簡化成這樣:

/v(any|number)

v 模式最接近大部分語言中的正則,所以這也是 learnvimscriptthehardway 書中推薦的使用方式。

理解 vim 中字元串轉義

通常如果你是直接使用命令,例如 /,? , :substitute 進行搜索的話,裡面的正則並不需要考慮轉義問題,但是你要使用 vimscript 編程就肯定要遇到轉義的問題。

vim 有兩種字元串表示方式,一種是使用單引號 , 另一種使用雙引號 "。

內的字元不會進行任何轉義,但是如果需要包含另一個 , 則需要使用兩個 連在一起表示。 " 內的 具有轉義功能,如果需要表示字面的 則需要兩個相連 。

幾個例子:

:echo abc =~# va{3}" 單引號不需要轉義 , 輸出 1 表示匹配成功:echo abc =~# "\v\a{3}"" 雙引號需要轉義 , 輸出 1 表示匹配成功:echo substitute("abc", v^(.*)[markdown]#39;, 1, ) " 輸出 abc,單引號需要兩個相連表示字面單引號:execute normal! ?v^#.* . "<cr>" " 使用拼接字元串同時利用單引號無需轉義以及雙引號轉義

理解 syntax 命令里的 pattern

:h syn-pattern 里有詳細介紹,簡單來說就是跟是用 :substitue 命令一樣,另外不包含 cpoption 的 l 總是不包含的(通常只要你有 vimrc 文件,cp 選項都是關閉的,所以一般用不著關心這個, 使用 :h cp 了解更多)

這裡要特別注意一個陷阱,考慮以下命令:

:syntax region Comment start="/*" end="*/"

為什麼雙引號裡面的 不需要轉義 ???

答案就是這裡的雙引號並不代表字元串,它只是邊界符號的一種表示,這類似你使用的 :substitue 命令,以下命令跟上面的是等效的:

:syntax region String start=+/*+ end=+*/+

syntax 裡面的正則同樣是默認 magic 的,注意你不能關閉這裡 magic,但是可以使用 v 讓它更加 magic,於是上面的命令可以這麼寫

:syntax region Comment start="v/*" end="v*/"

這裡的 v 有些多餘,但是可以讓你的代碼保持更好的一致性。

一些有用的匹配原

出了通常其它語言都有的 ^ $, vim 還提供很多非常有用的匹配原

< " 單詞開始,0寬度匹配> " 單詞結束,0寬度匹配zs " 設定匹配開始,0寬度匹配ze " 設定匹配結束,0寬度匹配\%^ " 文件開始,0寬度匹配\%$ " 文件結束,0寬度匹配{-} "非貪婪匹配多個,例如 .{-} 相當於 js 裡面的 .*?

這幾個是我個人日常最常用的,使用 :h ordinary-atom 可以查看全部匹配原

正則相關的 vimscript 方法

  • a =~# a 匹配返回 1,不匹配返回 0,不忽略大小寫
  • a =~? a 同上,但忽略大小寫
  • a !~# a 匹配返回 0,不匹配返回 1,不忽略大小寫
  • a !~? a 同上,但忽略大小寫
  • substitute( {expr}, {pat}, {sub}, {flags}) 使用 flags,替換 expr 裡面的 pat (即 pattern 表示的正則) 為 sub
  • match( {expr}, {pat}[, {start}[, {count}]]) 返回 pat 在 expr 裡面所匹配的位置,可設置開始位置和重複次數
  • matchend( {expr}, {pat}[, {start}[, {count}]]) 跟 match 函數一樣,但是返回最後一個字元的匹配位置
  • matchlist({expr}, {pat}[, {start}[, {count}]]) 返回匹配的列表,第一項是完整匹配,後面是其它子匹配項
  • matchstr({expr}, {pat}[, {start}[, {count}]]) 返回 expr 裡面 pat 所匹配的字元串,無匹配返回空字元串

  • split( {expr} [, {pat} [, {keepempty}]]) 使用 pat 分割字元串為列表

使用 :h functions 然後 /{pat} 可快速查看這些方法,使用類似 :h match() 可直接查看具體函數幫助。

grep 命令裡面的正則

vimgrep 使用 vim 的正則,當然也是開啟 magic 模式的。 但是 vimgrep 有一個嚴重問題就是 vimscript 本身運行效率低,所以導致搜索很慢,通常我們都會設置 grep 相關選項,然後使用 grep 命令調用外部工具進行搜索,例如設置 ag:

if executable(ag) set grepprg=ag --vimgrep $* set grepformat=%f:%l:%c:%mendif

vim 只會簡單的把搜索字元串評級到 ag 的命令里然後傳給 shell 去執行,所以現在的正則就是 ag 的正則了,它使用的是 PCRE正則, 以下命令:

:grep Pragmatic(?=Vim)

匹配 Vim 前面的 Pragmatic

vim 的正則確實比較複雜,希望本文能幫你更好的理解它們。

Happy vimming


推薦閱讀:

萬行以上文本的分組編號——在 VIM 中嵌入 python 和 perl 腳本
安裝YouCompleteMe --python
vim 雜談 - 關於快速編輯
如何使用vim整理聊天記錄?
如何使用Vim為每一行自動編號?

TAG:Vim |