Vim案例两则
在博問中看見兩個(gè)比較典型的Vim處理文本的案例,總結(jié)一下,希望對(duì)大家有所幫助。如果大家有好的方法,歡迎討論。尤其是第二個(gè)案例,如果能夠一個(gè)命令處理更好。
案例一
這是在博問中看到的一個(gè)問題,原始文本如下。
要求處理成下面的樣子,也就是把逗號(hào)從行尾移動(dòng)到下一行第一個(gè)單詞前面。
我的第一想法是先將2-6行的逗號(hào)刪除,然后在3-7行加上逗號(hào)即可。
刪除2-6行的逗號(hào)
:%s/,//解釋一下:
- : 冒號(hào)在Vim里面是命令引導(dǎo)符,想要輸入命令必須先輸入冒號(hào)。
- % 表示匹配所有行
- s/old/new/ 表示用new來替換old。s表示substitude(替換)。在s/,//中,old是逗號(hào),new是空(注意后面兩個(gè)斜線之間啥也沒有,表示空)。
在3-7行前面加上逗號(hào)
:3,7s/\</,/解釋一下:
- 3, 7 表示對(duì)第3行至第七行進(jìn)行處理,在Vim中m, n表示對(duì)第m到第n行進(jìn)行處理,比如:1,10d表示刪除1-10行
- s/\</,/ 表示用逗號(hào)替換\<,<表示單詞邊界,屬于特殊字符,需要轉(zhuǎn)義,所以是\<
這樣就搞定了,但是轉(zhuǎn)頭一想,有沒有更簡單的辦法呢?上面的辦法雖然也很簡單,但是需要兩次命令才能完成,有沒有辦法用一個(gè)命令搞定呢。搞了一會(huì),終于搞定,命令如下。
:%s/,\n\(\s*\)/\r\1,/解釋一下:
- %s/old/new/ 表示對(duì)所有行中的old用new來替換,這里每一行只有一個(gè)逗號(hào),所以不用加g,如果有多個(gè)逗號(hào),那么要變成%s/old/new/g的形式,g表示對(duì)每行中的所有滿足條件的文本進(jìn)行替換。也就是說%是縱向的,而g是橫向的。
- ,\n\(\s*\) 匹配逗號(hào),換行以及行首的若干空格。如下圖 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
- \r\1,匹配換行,行首的若干空格及逗號(hào),這里\1是回溯引用,表示前面第一個(gè)括號(hào)對(duì)應(yīng)的匹配,也就是行首的若干空格,因?yàn)樘砑佣禾?hào)時(shí)不能破壞這些空格,所以匹配的時(shí)候進(jìn)行保留以便替換時(shí)引用之。
注意,上面的(和)屬于特殊字符,需要轉(zhuǎn)義,如果覺得\(\)這種方式難看,可以在表達(dá)式前面加上\v。如此則特殊字符無需再轉(zhuǎn)義,可以像perl一樣使用正則表達(dá)式了。
:%s/\v,\n(\s*)/\r\1,/g案例二
也來來自于博問,要求將只包含5個(gè)字符(字母,數(shù)字,或者下劃線)的行,刪除其行尾的換行符。
原始文本如下:
處理之后如下:可見2,3,4行的換行符都被刪除了。
我首先想到的命令是
:g/\v^\w{5}$/ s/\n//g解釋一下:
整個(gè)表達(dá)式可以分成兩個(gè)主要部分。:g/\v^\w{5}$/設(shè)為A,s/\n//g設(shè)為B。那么這個(gè)命令的含義就是對(duì)于滿足條件A的行,執(zhí)行命令B。也就是對(duì)于所有只包含5個(gè)字符的行,刪除其行尾的換行符。
- :g/patter/ g表示global,patter是一個(gè)正則表達(dá)式,這句話的意思是對(duì)于所有滿足patter的行,用隨后地命令(即s/\n//g)進(jìn)行處理。
- \v^\w{5}$ 表示只包含5個(gè)字符的行,\v表示后面的特殊字符不用轉(zhuǎn)義,比如{}。^表示行首,$表示行尾。\w表示單詞字符,即字母,數(shù)字或者下劃線。
- s/\n//g 表示用空替換\n,也就是刪除換行符。
但是非常遺憾的是,執(zhí)行完這個(gè)命令后,結(jié)果并不正確,如下:
可以看到,第二個(gè)abcde末尾的換行并沒有刪除掉,為什么會(huì)這樣呢?仔細(xì)分析一下這個(gè)命令的執(zhí)行步驟。
首先Vim解析命令然后找到第一個(gè)滿足條件的行abcde,并刪除其末尾的換行符,此時(shí)文本變成下面的樣子。
這時(shí),第二行文本是abcdeabcde,共10個(gè)字符,已經(jīng)不再滿足5個(gè)字符的條件了,所以Vim不會(huì)再刪除其末尾的換行了,因而就產(chǎn)生了上面的錯(cuò)誤結(jié)果。
看來用一個(gè)命令處理有點(diǎn)困難,那就分成兩個(gè)吧。雖然麻煩一點(diǎn),但是結(jié)果正確。
首先把要處理的行加上一個(gè)特殊標(biāo)志,這個(gè)標(biāo)志能夠?qū)⒋幚淼男信c其它行區(qū)別開來,所以要選擇未在文本中出現(xiàn)的字符。另外一個(gè)要求是處理完之后這個(gè)標(biāo)志也要?jiǎng)h除,所以把它加在行尾,可以與換行符一并刪除。
先在包含5個(gè)字符的行末尾加上#
:%s/\v(^\w{5}$)/\1#/g刪除所有的#和換行符。
:%s/#\n//g由于這次不再以字符個(gè)數(shù)來甄別處理的行,而是以#號(hào)來定位,即使兩行合并之后,#號(hào)仍然存在,結(jié)果自然就正確了。你有更簡單的辦法么?
參考
關(guān)于跨行匹配,下面這個(gè)網(wǎng)頁上有詳細(xì)的介紹。
Vim, Search across multiplelines
總結(jié)
 
                            
                        - 上一篇: Oracle数据库之SQL连接查询
- 下一篇: C语言技巧之长度为0的数组
