Mon 16 Apr 2007
以前有人問過我,有沒有什麼方法可以在程式的每一行前面補上所在的行數?我的答案是:
cat -n input_filename > output_filename
他又問「如果沒有 cat 怎麼辦?可以用 vim 完成嗎?」因為我所提供的方法對於 Un*ix 的使用者來說是非常方便的,但是對其他作業環境的使用者可就未必了。那時候我想了一下,總覺得需要一個計數器,從頭開始算,然後把數字補在每一行的前面。雖然說 vim 有提供 :set nu 的功能,就像下面這兩張圖:
下面這張是原來的樣子

做過:set nu 之後

但是在windows 上你卻不能把它一行行 copy 下來用。不想搞程式,又想要一行解決,有沒有呢?
答案是必須要靠 vim 內建的函數來幫忙,也就是今天介紹的 line() 和 submatch()。
我們先來看答案:
:%s/^.*$/\=line(”.”) . ” ” . submatch(0)/g
在 vim 裡面, line() 就是用來代表行數的。而line()裡面的 “.” 則用來表示目前游標所在的地方,換言之,也就是處理到哪一行游標就在那。而 submatch(0) 則是用來表示前面所尋找的整個字串(pattern),而submatch(1)的話,則用來表示第一個以 \(…\) 夾起來的子字串。
不過由於我們所採用的是取代 s,s 的語法本來是 s/pattern1/pattern2/option,但是它提供在 pattern2的地方可以作一些運算。但是開頭必須要用 “\=” 來開始,否則就視之為字串,而且在這個情況下,\1 \2 這種特殊字串所代表的特殊意義都不能使用。一些細節你可以參考
:h sub-replace-expression
所以答案裡面的 pattern2 的部份就是先用 \= 做開始,表示要做運算,「”」這個符號夾起來的表示字串,而字串要和運作的結果相連的話,要用「.」來接。如果你的字串需要「”」這個符號,則用「\」補在前面,也就是變成「\”」。
結果就會像下面看到的一樣:
下取代指令:

最後需要的結果:

April 16th, 2007 at 6:14 pm
若只是這功能的話, 我想用 :%s/^/\=line(”.”).” “/ 應該會簡單一點吧…
April 16th, 2007 at 6:36 pm
如果行號想靠右對齊的話,可以用下面這一行 (假設以五個字元對齊)
:%s/^/\=printf(”%*d “, 5, line(”.”))/g
April 16th, 2007 at 10:01 pm
謝謝兩位的補充 :)
April 25th, 2007 at 3:49 pm
yjchou大人的方法在windows上行号出不来啊
April 27th, 2007 at 12:37 pm
May 31st, 2007 at 4:05 pm
想請問…
我是個VIM新手,最近抓了GVIM來用,
不過發現在存過檔後的檔案中使用倒退鍵都無反應!
(在還沒存過檔的檔案中或是在存過檔的檔案中"再鍵入"新的字元而後再對新的字元使用倒退鍵才有反應)
這是什麼地方的設定沒有調好呢?
懇請賜教!!
May 31st, 2007 at 4:27 pm
應該是您的 vim 設定的關係,不過因為在下不使用在 insert 模式下停止 backspace 的功能,無法回答您的問題,您可以使用在下的設定檔,或是移駕到 http://blog.gslin.org 去詢問 DK長輩的設定檔。
July 17th, 2007 at 2:03 pm
加行號應該是這樣吧.
:g/^/exec “s/^/”.strpart(line(”.”).” “, 0, 4)
前三個我在vim下都無法加行號。
July 17th, 2007 at 2:32 pm
windows version, vim 7.0 才能使用前面的一些方法。