vi/vim的巧妙使用-數值加減,數字遞增,序列等

vi/vim的巧妙使用-數值加減,遞增(轉),序列等

目錄

1. 函數式, 數字加減

在替換命令 s/// 中可以使用函數表達式來書寫替換內容,格式爲

:s/替換字符串/\=函數式

在函數式中可以使用 submatch(1)、submatch(2) 等來引用 \1、\2 等的內容,而submatch(0)可以引用匹配的整個內容。

示例之一:

:%s/\<id\>/\=line(".")                              " 將各行的 id 字符串替換爲行號
:%s/^\<\w\+\>/\=(line(".")-10) .".". submatch(1)    " 將每行開頭的單詞替換爲 (行號-10).單詞 的格式,
                                                    " 如第11行的 word 替換成 1. word

示例之二: 對於如下的markdown文本, 當需要將章節號+1時,

# 1. 指南
## 1.0 資源
...
## 1.1 架構
...
# 2. 實踐
...

執行如下替換即可:

%s/#\+\s*\zs\d*\ze\./\=submatch(0)+1/


2. 遞增.方法一

轉載自:https://blog.easwy.com/archives/vim-tips-advanced-substitute-1/
這個技巧是在VIM郵件列表中看到的,非常實用。

這裏以編寫C語言程序爲例, 假設,我們最終想完成的代碼如下:

#define BIT_MASK_1      (1 << 0)
#define BIT_MASK_2      (1 << 1)
#define BIT_MASK_3      (1 << 2)
#define BIT_MASK_4      (1 << 3)
#define BIT_MASK_5      (1 << 4)
#define BIT_MASK_6      (1 << 5)
#define BIT_MASK_7      (1 << 6)
#define BIT_MASK_8      (1 << 7)
#define BIT_MASK_9      (1 << 8)
#define BIT_MASK_10      (1 << 9)
#define BIT_MASK_11      (1 << 10)
#define BIT_MASK_12      (1 << 11)
#define BIT_MASK_13      (1 << 12)
#define BIT_MASK_14      (1 << 13)
#define BIT_MASK_15      (1 << 14)
#define BIT_MASK_16      (1 << 15)
#define BIT_MASK_17      (1 << 16)
#define BIT_MASK_18      (1 << 17)
#define BIT_MASK_19      (1 << 18)
#define BIT_MASK_20      (1 << 19)
#define BIT_MASK_21      (1 << 20)
#define BIT_MASK_22      (1 << 21)
#define BIT_MASK_23      (1 << 22)
#define BIT_MASK_24      (1 << 23)
#define BIT_MASK_25      (1 << 24)
#define BIT_MASK_26      (1 << 25)
#define BIT_MASK_27      (1 << 26)
#define BIT_MASK_28      (1 << 27)
#define BIT_MASK_29      (1 << 28)
#define BIT_MASK_30      (1 << 29)
#define BIT_MASK_31      (1 << 30)
#define BIT_MASK_32      (1 << 31) 

我們不需要一行一行的去寫,只需要先寫好第一行,如下:

#define BIT_MASK_1      (1 << 0) 

然後,我們回到Normal模式,在這一行上輸入”Y31p”,拷貝此行,然後粘貼31次。這樣,我們得到總共32行上面的內容。

2.1 命令

現在使用”V31j”命令選中這32行,然後使用兩次替換命令:

:'<,'>s/BIT_MASK_\zs\d*\ze/\=line(".") - line("'<") + 1
:'<,'>s/\zs\d*\ze)$/\=line(".")-line("'<")
【或】
:%s/BIT_MASK_\zs\d*\ze/\=line(".") - line("'<") + 1
:%s/\zs\d*\ze)$/\=line(".")-line("'<") 

這樣,我們就得到了我們想要的結果。

這種方式還可以用於數組下標的自動增加,以及文本的章節自動編號等功能。只要你能夠用正則表達式準確的定位出你想要自動編號的的數字,那麼就可以使用這種方法來自動編號。

2.2 解析

我們使用了兩條VIM替換命令,下面詳細剖析一下這兩條命令。

以第一條命令爲例,第二條命令和第一條命令類似:

:'<,'>s/BIT_MASK_\zs\d*\ze/\=line(".") - line("'<") + 1 

這條命令在我們選中的區域內進行替換,查找以”BIT_MASK_”開頭,後面跟任意多個數字的字符串,並把匹配位置放在數字上,然後使用後面表達式計算出來的數字替換這些匹配的數字。

下面是這條命令中每個元素的含義:

'<,'>        我們所選中的區域 (:help '<,:help '> )
s            在選中的區域中進行替換 (:help :s )
\zs          指明匹配由此開始 (:help /\zs )
\d*          查找任意位數的數字 (:help /\d )
\ze          指明匹配到此爲止 (:help /\ze )
\=           指明後面是一個表達式 (:help :s\= )
line(".")    當前光標所在行的行號 (:help line() )
line("'<")   我們所選區域中第一行的行號 (:help line() ) 
'<和'>是我們使用了"v","V"命令選中一個visual區域後,VIM設置的標記,分別用來標識visual區域的開始和結束。

“BIT_MASK_\zs\d*\ze”是一個正則表達式,用來查找以”BIT_MASK_”開頭,後面跟任意多個數字的字符串。其中”\zs”、”\ze”用來指定匹配的開始和結束位置,因爲我們只打算替換”BIT_MASK_0”中的數字,所以在查找時只把匹配區域置在數字上。

由於我們的替換操作要把不同行的數字替換成不同的值,所以在這裏需要使用一個表達式來計算出替換後的值。當”:s”命令的替換字符串是以”\=”開頭時,表明使用一個表達式計算的結果進行替換。我們這裏的表達式就是”line(“.”) - line(“’<”) + 1”,其中”line()”函數用來獲得行號,它可以獲得當前行的行號,以及指定的標記(mark)所在的行號。”line(“.”)”用來獲得當前光標所在行的行號,”line(“‘<”)”則用來獲得”’<”標記所在行的行號。這兩個行號的差加上1就是我們想替換的值。


3. 遞增: 方法二

轉載自:https://blog.easwy.com/archives/vim-tips-advanced-substitute-1/
下面將介紹另外一種方法來完成同樣的功能。

我們使用VIM的替換功能,實現高效的代碼編寫。本文將介紹另外一種方法,實現相同的功能。

我們先看例子:

UniqueID2       = lview.focusedItem.subItems.opIndex(0).text;
Parent          = lview.focusedItem.subItems.opIndex(0).text;
Children        = lview.focusedItem.subItems.opIndex(0).text;
login           = lview.focusedItem.subItems.opIndex(1).text;
txtCust.text    = lview.focusedItem.subItems.opIndex(2).text;
txtProj.text    = lview.focusedItem.subItems.opIndex(3).text;
txtbDate.text   = lview.focusedItem.subItems.opIndex(4).text;
txtdDate.text   = lview.focusedItem.subItems.opIndex(5).text;
txteDate.text   = lview.focusedItem.subItems.opIndex(6).text;
txtPM.text      = lview.focusedItem.subItems.opIndex(7).text;
txtLang.text    = lview.focusedItem.subItems.opIndex(8).text;
txtVendor.text  = lview.focusedItem.subItems.opIndex(9).text;
txtInvoice.text = lview.focusedItem.subItems.opIndex(10).text;
txtPMFund.text  = lview.focusedItem.subItems.opIndex(11).text;
txtProjFund.text= lview.focusedItem.subItems.opIndex(12).text;
txtA_No.text    = lview.focusedItem.subItems.opIndex(13).text;
txtNotes.text   = lview.focusedItem.subItems.opIndex(14).text;
txtStatus.text  = lview.focusedItem.subItems.opIndex(15).text; 

我們要把上面代碼中括號中的數字,替換成由0開始的一個順序遞增序列,例如:

UniqueID2       = lview.focusedItem.subItems.opIndex(0).text;
Parent          = lview.focusedItem.subItems.opIndex(1).text;
Children        = lview.focusedItem.subItems.opIndex(2).text;
......

實現以上需求,除了用前面介紹的方法外,還可以用下面的命令:

:let n=0 | g/opIndex(\zs\d\+/s//\=n/|let n+=1 

這條命令同上一篇介紹的命令類似,它也使用了VIM的替換功能和表達式,不同之處在於它並不需要事先選中替換區域,因爲它沒有使用”line()”函數來計算當前位置的偏移,而是直接使用變量來進行賦值。

下面簡單講解一下這條命令各個組成元素:

let          爲變量賦值 (:help let )
|            用來分隔不同的命令 (:help :bar )
g            在匹配後面模式的行中執行指定的ex命令 (:help :g )
\zs          指明匹配由此開始 (:help /\zs )
\d\+         查找1個或多個數字 (:help /\d )
s            在選中的區域中進行替換 (:help :s )
\=           指明後面是一個表達式 (:help :s\= ) 

所以,這條命令的執行過程爲:

給變量n賦值爲0;
查找模式"opIndex(\zs\d\+",使用變量n的值替換匹配的模式字符串;
給變量n加1;
回第二步;
需要說明一下"|",它用來分隔不同的命令。

另外,在substitute命令中,如果省略匹配模式字符串,它會使用之前定義的匹配模式字符串,在本例中就是由”global”命令定義的”opIndex(\zs\d+”。

除了上面介紹的方法外,還有一個VIM插件專門實現數字、日期等的增、減,可以在下面的網址下載此插件:

http://vim.sourceforge.net/scripts/script.php?script_id=670
或
http://mysite.verizon.net/astronaut/vim/index.html#VISINCR

4. 兩個小技巧(統計字數,將十進制轉換爲16進制數字)

再介紹兩個小技巧。

這兩個技巧是近期看到的,記錄下來,以免忘記。

4.1 [技巧一] 統計文章字數

第一個是在VIM郵件列表中看到的,給出了一個如何統計文章字數的方法。

統計一個完整文件的字數,可以使用Unix下的wc工具,它能夠統計一個文件的行數、單詞數和字符數。

如果只想統計一個特定的模式出現的次數,wc工具就無能爲力了,這時候可以用VIM的替換功能。

假定想統計文章中出現的單詞的數目,可以使用下面的命令:

:%s/\w//gn

簡單解釋一下,這種命令實際上是報告整個文件中出現的單詞的數目,命令分解如下:

%s            在整個文件中替換 (:help :s )
\w           匹配一個字 (word) (:help /\w )
g           替換行內所有出現的匹配 (:help :s_flags)
n           只報告匹配的數目,並不真正進行替換(:help :s_flags)

如果是使用LaTeX寫論文的話,可以用這種方式排除LaTeX的控制字符,統計出論文中的實際字數,參考文檔列出的郵件中給出了這樣的一個示例。

在VIM中“:help count-items”以及“:help count-bytes”,可以看到更多統計數目的方法。

4.2 [技巧二] 將一串十進制數字轉換爲16進制數字

這個技巧是在水木社區的VIM版看到的(http://www.newsmth.org/bbsdoc.php?board=VIM )。

這個文章是關於如何將一串十進制數字轉換爲16進制數字,使用VIM完成轉換的最簡單方法如下:

:%s/\d\+/\=printf("%X", submatch(0))/g

這條命令的原理是,把一串數字,用printf()函數的輸出替換掉,printf()函數輸出的正是這串數字的16進制形式。

分解如下:

%s            在整個文件中替換 (:help :s )
\d\+            匹配一個或多個數字 (:help /\d  :help /\+ )
\=           使用表達式的結果進行替換 (:help /\w )
printf        按指定格式輸出 (:help printf() )
submatch()    返回:s命令中的指定匹配字符串 (:help submatch() )
g           替換行內所有出現的匹配 (:help :s_flags)
看來,替換命令的巧妙使用可以完成很多意想不到的功能!

5. 數字序列(range函數)

有時候, 會需要生成類似於下面這種數字序列:

5
6
7
...
100

在vim裏,要生成這個序列,有很多種方法,其中一種是:

:s/^/\=range(5, 100)

類似地,可以生成步進爲2的數字序列:

:s/^/\=range(5, 100, 2) 

   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章