VIM中的Regex__vim

來源:互聯網
上載者:User

VIM作為一款編輯軟體有著強大的操作指令,靈活的配置方法,通過適當的組合能夠實現令人眼花繚亂的功能,而Regex作為一門處理文本和資料的重要工具,和VIM異曲同工,通過元字元的簡單組合就可以匹配千變萬化的文本和資料,它是如此的強大以至於有些任務如果沒有Regex幾乎沒有其他好的方法實現。下面看看這兩個強大的武器是如何結合在一起的。

本文翻譯自http://www.vimregex.com/,算是一篇比較全面的VIMRegex介紹。 2.介紹 2.1什麼是VIM。

VIM(VI Improve)是VI編輯器的改進版,它在UNIX中無處不在。VIM由Bram Moolenaar發明,是一款免費的編輯器,當然如果你願意,可以捐助一部分錢。

VIM有自己的網站為www.vim.org和郵件清單,上面的資料涵蓋VIM的方方面面。目前,VIM能夠運行在各大作業系統上,甚至是一些linux髮型版本(redhat)的預設編輯器。

VIM擁有現代編輯的許多特點:文法高亮、可以定製化的使用者介面、可以方便的與多種IDE整合在一起,從而具有一些更加迷人的特色,比如故障恢複、自動命令補全、會話管理等。

VIM擁有龐大的使用者群,僅linux使用者就超過1000萬,這個數字還在進一步增加。 2.2關於本教程

之所以寫這個教程,只是因為我愛Regex,沒有什麼能比寫出一個精心設計滿足需要的Regex更讓人興奮的了,我希望這能作為一個引言。

不過說真的,Regex作為一個處理文本和資料的工具不是獨立存在,而是嵌入在其他的程式語言或工具中,比如UNIX中的著名的grep程式,它根據一定的模式尋找檔案中的內容。你可以把Regex看做一種模式比對語言,用它來處理一些棘手的文本問題會有意想不到的效果。 2.3致謝

感謝Benji Fisher, Zdenek Sekera, Preben “Peppe” Guldberg, Steve Kirkendall, Shaul Karl(排名不分先後)以及所有給我建議的人。

如果你有好的建議或者想法,隨時發信給我(olontir at yahoo dot com)。 3.替換命令 3.1尋找/替換

:range s[ubstitute]/pattern/string/cgiI
c 每次替換都要確認
g 替換一行當中所有的匹配項(沒有g只替換第一個匹配值,pingao註:注意與%區別)
i 忽略大小寫
I 不忽略大小寫

[]中表示可選項 3.2範圍操作、行地址及標記

在講匹配模式之前,先來瞭解下行地址。一些命令可以接受行範圍,這樣命令就會限定在這個範圍內執行。行範圍通常由逗號(,)或者分號(;)分隔的標示符組成,你也可以使用命令mI在當前位置作一個標記,以方便後面使用,”I”可以是任何字母。

標示符 說明
數字 行號
. 當前行
$ 檔案的最後一行
% 整個檔案,與1,$相同
‘t 標記t
/pattern[/] pattern的下一個匹配行
?pattern[?] pattern的上一個匹配行
\/ 最近一個搜尋pattern的下一個匹配行
\? 最近一個搜尋pattern的上一個匹配行
\& 最近一個替換pattern的下一個匹配行

如果沒有指定行,操作只針對當前行。

這裡有一些例子,

10, 20

-10到20行

/Section 1/+,/Section 2/-

-所有Section 1和Section 2之間的行,不包括它們所在行,+標示加一,-標示減一,可以重複多個

:/Section/+ y

-複製Section的下一個匹配行

:// normal p

-粘貼到Section下一個匹配行的下一行

Tip1:如果你在pattern裡使用/,一定要使用\進行轉義,比如,
s/\/dir1\/dir2\/dir3\/file/dir4\/dir5\/file2/g

為了避免這種令人迷惑的轉義災難,VIM中可以自訂分隔字元,我喜歡用冒號(:)

Tip2:將下面兩個快速鍵映射放在你的vimrc檔案中,
noremap ;; :%s:::g<Left><Left><Left>
noremap ;' :%s:::cg<Left><Left><Left><Left>

有了這兩個快速鍵,你會省去不少敲擊鍵盤的時間,它會直接定位到搜尋模式那裡,輸入搜尋部分後再輸入替換部分然後按斷行符號鍵。第二個快速鍵增加了確認標誌。 4.模式說明 4.1錨

假設你想把所有的vi替換為VIM,很容易會想到下面的命令,

s/vi/VIM/g

但是如果你真的這麼做了,你會發現,它會把所有vi替換為VIM,甚至vi只是某個單詞的一部分,這可能不是你想要的。

你可能還會想到,在vi兩邊添加空格來達到想要的效果,

s: vi : VIM :g

你會發現結果並沒有變化,正確的方法是使用單詞邊界標誌\<\>,

s:\<vi\>:VIM:g

行開始和結束有自己的標識符^和$,替換所有在行開始出現的vi,

s:^vi\>:VIM:

如果一行之中只有vi則進行替換,

s:^vi$:VIM:

現在假設你不僅要替換vi還要替換Vi、VI,有幾種方法可以實現, 最簡單的方法是使用i標誌, %s:vi:VIM:gi 定義字元類(character class),:%s:[Vv]i:VIM:將會替換所有的Vi和vi 4.2逸出字元或元字元

到目前為止,所有的匹配模式(pattern)都是由一些正常字元組成的,而Regex的真正強大之處就在於元字元(metacharacter),元字元是是指一些具有特殊含義的字元,從外觀上它們的前面常有一個反斜線,如下表所示,

# 匹配 # 匹配
. 除分行符號之外的任一字元
\s 空白字元 \S 非空白字元
\d 數字 \D 非數字
\x 十六進位 \X 非十六進位
\o 八進位 \O 非八進位
\h 單詞頭(a-zA-Z_) \H 非單詞頭
\p 可列印字元 \P 非列印字元
\w 單詞字母 \W 非單詞字母
\a 字母 \A 非字母
\l 小寫字母 \L 非小寫字母
\u 大寫字母 \U 非大寫字母

比如,你想匹配 09/01/2000,可以使用下面的Regex,

\d\d/\d\d/\d\d\d\d

匹配一個首字母大寫的六字母單詞,

\u\w\w\w\w\w

如果你想匹配一個不知道長度的單詞或者一個長單詞,寫出每個\w不是很方便,這就要用到下面介紹的量詞(quantifiers)概念了。 4.3量詞、貪婪匹配與惰性匹配

將一個量詞(quantifiers)放置在模式(pattern)一部分後面,就可以限制這部分的重複次數。

量詞 說明
* 0個或多個,.*匹配任何東西,甚至一個空行
\+ 1個或多個
\= 0個或1個(pingao註:相當於?)
\{n, m} 匹配n到m次
\{n} 匹配n次
\{, m} 匹配0到m次
\{n, } 至少匹配n次

n和m都必須是正整數

現在很容易就能寫出一個匹配任意長度單詞的運算式:\u\w\+。

上面這些量詞都是工作在貪婪模式下的,它們會儘可能多的匹配字元。有時候這會帶來意想不到的問題,考慮一個典型的例子,假如你想匹配一個含有某種限定符的文本,比如被引號或者括弧包圍的文本,因為你不知道這些限定符裡有什麼,我們可以使用/".*"/。

但是這個運算式將會匹配任何處於第一個引號和最後一個引號中間的文本,如粗體標註的部分

this file is normally “$VIM/.gvimrc”. You can check this with “:version”.

這種問題可以使用惰性(non-greedy)量詞來解決,

量詞 說明
\{-} 0個或多個,儘可能少的匹配
\{-n,m} n個或多個,儘可能少的匹配
\{-n, } 至少匹配n次,儘可能少的匹配
\{-, m} 至多匹配m次,儘可能少的匹配

讓我們用\{-}替換上面的*,所以.\{-}將會匹配第一個引號的內容。

this file is normally “$VIM/gvimrc”. You can check this with “:version”.

\{-}確實沒有讓我們失望,下面看看執行下面的命令將會發生什麼,

:s:.\{-}:_:g

執行前:

n and m are decimal numbers between

執行後:

n a_n_d m a_r_e d_e_c_i_m_a_l n_u_m_b_e_r_s b_e_t_w_e_e_n

“儘可能的少的匹配”在這裡的意思是匹配0個字元,然而匹配竟然發生在了字元之間,下面我引用Bram自己的話來解釋這種行為,

匹配到0個字元也是一種匹配,因此它會將0字元替換為一個”_”,然後走到下一個位置,繼續匹配到0個字元。

大部分情況下,\{-}沒有多大的用處,它的這種運行方式主要是為了和*保持一致,後者也會匹配0個字元,相比之下,x\{-1,}是一種更加沒用的寫法,它只會匹配一個x,和x功能一樣,比較有用的一種寫法為x\{70},至於x\{-3,}", "x\{-2,}", "x\{-1,}用處也不大,只是為了和貪婪模式的量詞保持一致。

                              -Bram

但是如果你只想匹配第二個引號的內容呢。或者我們只想改變引號中的一部分內容呢。我們將會用到分組(grouping)和反向引用(backreference),在這之前我們先來看下字元區間的概念(character range)。 4.4字元區間

典型的字元區間:

[012345]將會匹配括弧中的任意一個,[0-5]與之等價,類似地,我們可以定義全部小寫字母的字元區間[a-z],所有的字母[a-zA-Z],數字加字母[0-9a-zA-Z],根據你所在的地區,你可以在字元區間添加à, Ö, ß這樣的非ASCII字元。

注意字元區間僅僅匹配其中的一個字元,[0123]和0123不同,順序對於一個字元區間不重要,[0123]和[0231]一樣,而0123和0231是兩個截然不同的模式。看看執行下面的句子會發生什麼,

s:[65]:Dig:g

執行前:

High 65 to 70. Southeast wind around 10

執行後:

High DigDig to 70. Southeast wind around 10

然後執行

s:65:Dig:g

執行前:

High 65 to 70. Southeast wind around 10

執行後:

High Dig to 70. Southeast wind around 10

通過放置一個反選符號(^)在字元區間的最前面可以很容易的去除不願匹配的字元,下面將會匹配除大寫字母外的任一字元,

/[^A-Z]/

我們可以使用字元區間重寫匹配引號內的文本,

/"[^"]\+"/

注意[]內部的元字元會失去其特殊的意義,所以如果你想要一個包含-的字元區間,把-放在最前面,如下運算式將會匹配所有的數字和-,

/[-0-9]/

同時^如果不在最前面,也會失去其特殊意義。

現在考慮一個現實的例子,假設有一個文法檢測器想找出所有不以大寫字母開頭的句子,下面的運算式可以實現這一點,

\.\s\+[a-z]

這將會匹配一個句號、一個或多個空格然後是一個小寫字母,我們現在知道如何找到錯誤,下面來看看如何修複它。這裡就需要我們記住前面的匹配值以便後面可以重新調用它,這就是反向引用大顯身手的地方了。 4.5分組和反向引用

你可以使用\(\)對模式比對項進行分組,然後通過\1, \2 ... \9來引用。一個典型的例子為交換每一行的頭兩個單詞,

s:\(\w\+\)\(\s\+\)\(\w\+\):\3\2\1:

\1代表第一個單詞,\2代表一個或多個空白符,\3代表第二個單詞。如何知道哪個數字代表哪個匹配項,從左往右數\(的個數。

# 含義 # 含義
& 模式比對到的全部內容 \L 將後面的字元都轉換為小寫
\0 同上 \U 將後面的字元都轉換為大寫
\1 第一個括弧中匹配的內容 \E end of \U and \L
\2 第二個括弧中匹配的內容 \e end of \U and \L
\r 將一行分為兩行
\9 第九個括弧中匹配的內容 \I 將下一個字元轉換為小寫
~ 前面替換的字串 \u 將下一個字元轉換為大寫

看下上面的語法檢查問題完整的運算式,

s:\([.!?]\)\s\+\([a-z]\):\1 \u\2:g

我們將0個或多個空白符替換為兩個空格。 4.6備選

備選(alternation)是指用\|將多個運算式結合在一起,這樣一旦有一個運算式匹配到,則整個運算式匹配成功,返回這個運算式匹配內容。(pingao註:類似於邏輯操作符|)

\\(Date:\|Subject:\|From:\)\(\s.*\)

上面的運算式將會把郵件的頭部和內容放在\1和\2中,對於備選需要注意的是,它不是貪婪匹配的,一旦多個運算式有一個運算式匹配到,後面的運算式將不再匹配,這意味著對於一個備選,運算式的順序十分重要。

Tip3:將\(\)快速的放在運算式中,
cmap ;\ \(\)<Left><Left> 4.7Regex操作符的優先順序

和算數運算式一樣,Regex的運算子也有一定的優先順序,下表從高到低列出了各個操作的優先順序,

優先順序 操作符 說明
1 \(\) 分組
2 \=,\+,*,\{n} 量詞
3 abc\t\.\w 字元、元字元
4 \| 備選
5.全域命令 5.1全域搜尋及執行

我想介紹另一個用處廣泛功能強大的命令,

:range g[lobal][!]/pattern/cmd
在range的範圍內,在pattern匹配行執行Ex cmd(預設為:p[rint]),如果pattern前面加上一個!,表示pattern沒有匹配的行。

全域命令的工作原理為,第一遍掃描range範圍的每一行,並對pattern匹配行做一個標記;第二遍對每一個標記行執行cmd。range預設為整個檔案。

注意:Ex command包括所有你在VIM命令列輸入的命令,比如

:s[ubstitute], :co[py] , :d[elete], :w[rite]

非Ex command(normal command)也可以執行,

:norm[al]non-ex command 5.2例子

:g/^$/ d

-刪除檔案中所以的空行

:g/^$/,/./-j

-將多個空行轉換為一個空行

:10,20g/^/ mo 10

-顛倒10到20行的順序

下面是一個來自 Walter Zintz vi教程的例子,例子有改動

:'a,'b g/^Error/ . w >> errors.txt

-在標記’a和’b之間找到以Error開始的行,然後將這些行追加到errors.txt。注意:w前面的.(當前行)不要漏掉,否則將會把整個檔案追加到errors.txt中。

你可以使用|作為分隔字元執行多個命令,如果你想在參數中使用|,要用\對其進行轉義。 Zintz的另一個例子,

:g/^Error:/ copy $ | s /Error/copy of the error/

將所有Error行拷貝到檔案的最後,然後將Error替換為copy of the error。s命令沒有指定地址,預設為當前行。

:g/^Error:/ s /Error/copy of the error/ | copy $

將上面的操作順序顛倒了一下,先替換後複製。 6.更多的例子 6.1小貼士

(1)由Antonio Colombo提供

去掉所有行尾部的空白符,

s:\s*$::或者s:\s\+$:: 6.2建立一個大綱

這個例子需要你有點html的背景,我們需要將<h1>和<h2>標籤中的標題和副標題分離出來,做一個表格。

(1)首先我們給每個標籤做一個標記,<h1><a name="anchor">Heading</a></h1>,anchor是標籤的唯一標示,實現運算式如下,

:s:\(<h[12]>\)\(.*\s\+\([-a-zA-Z]\+\)\)\s*\(</h[12]>\):\1<a name="\3">\2</a>\4:

說明:

(2)接下來,將標題拷貝到一個地方,

:%g/<h[12]>/ t$

上面的命令將會把<h1>和<h2>標籤所在行拷貝到檔案的最後。現在檔案的樣子如下,

<h1><a name="anchor1">Heading1></a></h1><h2><a name="anchor2">Heading2></a></h2><h2><a name="anchor3">Heading3></a></h2>..........................<h1><a name="anchorN">HeadingN></a></h1>

第一步,為了要把表格的元素連結到各自的位置,我們要把name="替換為href="#。

s:name=":href="#:

第二步,要讓h1與h2看起來不同,我們定義”majorhead”和”minorhead”兩個CSS類,

g/<h1>/ s:<a:& class="majorhead":g/<h2>/ s:<a:& class="minorhead":

現在檔案看起來像這樣,

<h1><a class="majorhead" name="anchor1">Heading1></a></h1><h2><a class="minorhead" name="anchor2">Heading2></a></h2>#(pingao註:我認為此時檔案應該是這樣的)<h1><a class="majorhead" href="#">Heading1></a></h1><h2><a class="minorhead" href="#">Heading2></a></h2>

我們不再需要<h1>和<h2>標籤,

s:<h[21]>::

替換</h1>和</h2>標籤為<br>,

s:/h[21]:br:

現在檔案的樣子,

<a class="majorhead" name="anchor1">Heading1></a><br><a class="minorhead" name="anchor2">Heading2></a><br>#(pingao註:我認為此時檔案應該是這樣的)<a class="majorhead" href="#">Heading1></a><br><a class="minorhead" href="#">Heading2></a><br>
6.3處理表格

很多情況下,你需要處理表格形式的文本。例如,下面的文本,

 Asia    America  Africa   Europe Africa  Europe   Europe   Africa Europe  Asia     Europe   Europe Asia    America  Africa   Europe Africa  Europe   Asia     Africa Europe  Asia     Asia     Europe Europe  America  Africa   Asia Africa  Europe   Europe   Africa Europe  Asia     Europe   Europe

假設你想把第三列的”Europe”替換為 “Asia”,

:%s:\(\(\w\+\s\+\)\{2}\)Europe:\1Asia:

交換前兩列,

:%s:\(\w\+\)\(.*\s\+\)\(\w\+\)$:\3\2\1:

未完待續… 7.其他語言Regex特點

現在我將VIM的Regex與其他語言的Regex做一個對比,特別是Perl。提起Regex,Perl肯定不得不提。

(在Steve Kirkendall的協助下整理)Perl和VIM的主要區別為, Perl的大多數元字元不需要反斜線。個人認為,反斜線越少越好,這樣Regex會更加可讀。 Perl中你可以在量詞後加上一個?將貪婪模式的量詞轉換為非貪婪模式, 比如*?為非貪婪模式的*。 Perl的Regex支援各種奇怪的選項。 Perl的Regex可以包含變數,變數將會替換為具體的值,這稱作”變數替換”。 8.連結

在正常模式下,輸入”:help pattern”,閱讀VIM協助文檔的Regex和搜尋章節。

市場上有兩本不錯的介紹VIMRegex的書, “Learning the vi Editor” by Linda Lamb and Arnold Robbins. “vi Improved - VIM” by Steve Oualline

Jeffrey Friedl的”Mastering Regular Expressions”是一本Regex的權威指南,此書主要介紹Perl的Regex,由O’Reilly出版,官網上有一章免費。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.