vim如何有效處理制式內容?

如我有以下資料

apple
bird
cat
dog

想要得到以下這樣的代碼

apple=listdata["name"]["apple"]
bird=listdata["name"]["bird"]
cat=listdata["name"]["cat"]
dog=listdata["name"]["dog"]

覺得一個一個複製再修改沒發揮到vim的強大功能,除了用正則表達式還有什麼辦法嗎?


用宏是正確的,對於行數不多的情況可以輕易完成。

但如果行數比較多,那麼宏就會很慢(就算是開啟了 lazyredraw )。我嘗試過對 10000 行左右的文本進行上述操作,已經有明顯的延遲。

通常情況下,這種任務是建議使用 awk 或者 sed 來完成。如果因為某些原因無法或者不方便用的,選擇 vim 來完成也是推薦使用正則表達式替換,而不是用宏,試試使用下面的替換命令,你可以看到處理速度的巨大差距,內容越多,差距越明顯:

:%s/.*/=listdata["name"][""]

不知道出於什麼原因,題主要求不使用正則表達式來完成,所以 %s 就無法使用。但是,我們還是有曲線救國的方法——利用 ex 命令和寄存器。

直接使用宏慢的原因在於所有的鍵入操作都需要不斷被重複,想要加快速度,那麼關鍵就是在於減少鍵入的次數。

在原來的宏中,我們需要鍵入 27 個按鍵。

ywA=listdata["name"]["&0"]

重複次數越多的時候,所需要消耗的性能就越多。

所以,我們可以利用寄存器來保存那些需要重複輸入的部分,讓操作簡化。

:let @a="listdata["name"][""
:let @b=""]"

然後利用 normal 命令來直接運行操作,而不是直接執行寄存器(注意,vim 中的「宏」就是對記錄在寄存器中的按鍵的執行,所以你可以直接編寫宏,或者把宏用到其他命令中,宏只是寄存器的一種應用。)

:%norm yw$"ap"0p"bp

這種方式比使用宏好的地方在於,你不需要知道應該執行多少次宏,[range] 對於 normal 命令也適用,這個命令會自動從第一行執行到行末。

儘管你可以看到性能比之前有來明顯的提升,但是明顯還是比使用 %s 要慢,因此,在通常情況下,使用替換都會是更好的選擇。

那麼,在不能用正則表達式的情況下,我們真的就無法使用 substitute 了嗎?

當然不是,我這裡還有一個解決方案——利用 global! 命令調用 substitute ,你可以得到和 %s 接近的執行速度。

在本題中,使用 substitute 的難點在於:

  • 不能使用正則表達式所以無法構造 {pattern}
  • 由於無法構造 {pattern} ,所以 substitute 無法使用,global 也無法直接使用
  • execute 命令只能針對當前行,無法使用 [range] ,所以無法通過 execute 調用 substitue 配合 getline() 繞過正則表達式

所以解決之道就是繞過 {pattern} 這道坎,我想到的解決方案是這樣:

  • 使用任意一個不在文檔中的字元(我這裡使用了「+」)作為 {pattern} ,利用 global! 反向匹配到所有行
  • 使用 execute 命令調用 substitute ,這樣就能使用 getline() 函數獲取每一行的內容,而不需要用到正則表達式

:v/+/exe "s/".getline(".")."/=listdata["name"][""]"

這樣我們就能繞過正則表達式來使用 substitute ,得到比使用宏更快的處理速度。

最後的最後,拋開 vim 本身的方法,如果你的電腦上還裝有 Python,那麼你就可以通過 Python 來處理。注意,我這裡說的是並不是「寫一個 Python 腳本來處理」,而是「在 vim 中直接調用 Python 來處理」。

首先,確保你的 vim 打開了 Python 介面支持

:version "看到 +python
:echo(has("python")) "返回 1

然後直接在命令欄中寫

py for i in range(len(vim.current.buffer)):vim.current.buffer[i]=vim.current.buffer[i]+"=listdata["name"][""+vim.current.buffer[i]+""]"

速度比 vim 內建的方法都要高,比 %s 都要快,而且沒有用到正則表達式,完美符合題主的要求。


文本替換什麼的太低端,一句`:"&<,"&>s/(w*)/1=listdata["name"]["1"]/g`就搞定了會不會太無聊?

我來點不一樣的吧 ^_^

在文本前後輸入一些代碼使看起來這樣:

s = """
apple
bird
cat
dog
"""
print "
".join(x+"=listdata["name"][""+x+""]" for x in s.split("
") if x)

然後,選中所有這些文本,然後按下`!python`,回車即可見到效果。

以上是用python舉例,用其它腳本同理。

PS:這裡只是說怎麼解決題主的問題,當然實際情況下我從不會這麼干,我寧可在代碼里用eval。。。


用宏!

1. 滑鼠放在第一行

2. 0yiw

3. A=listdata["name"]["

4. Esc p

5.A"]

apple=listdata["name"]["apple"]

bird=listdata["name"]["bird"]

cat=listdata["name"]["cat"]

dog=listdata["name"]["dog"]


@李繼剛 的回答一致,這裡再補充說明一下。

使用「Record and playback commands」,用q啟用寄存功能,然後對第一行進行修改,這時修改記錄在寄存器內。最後對剩下幾行用寄存器裡面的記錄進行同樣的修改。

1. 游標在第一行的任意位置,點擊qa,開始記錄操作;

2. 0yiw (將游標置於行首,複製單詞);

3. A=listdata["name"][" (在行尾追加=listdata["name"][");

4. Esc p (回到normal模式,粘貼2中複製的單詞);

5. A"](在行尾追加"]);

6. Esc j(回到normal模式,將游標移動到下一行);

7. q(停止記錄);

8. 3@a(調用記錄的操作3次)

參考:Vim的user-manual中「Making big changes」一節。


用word的郵件合併(逃→_→)


方法挺多的。推薦一個插件:Multi-cursor.

Multi-cursor操作:

  1. shift +v選中要操作的內容
  2. CTRL + n: 使用Multi-cursor插件
  3. I:行首插入
  4. 繼續1,2兩部操作
  5. A: 行末插入

無插件方法:

  1. ctrl + v :進入Visual Block模式,選中需要操作的內容
  2. I:在行首輸入入 並輸入要name=listdir["name"]["
  3. ctrl + v:進入 visual Block,選中整塊內容
  4. A: 行末輸入"]

拖拽至此處上傳


術業有專攻,請用awk sed


用正則表達式!


推薦閱讀:

如何自己開發/集成一個IDE?
你最喜歡的命令是什麼?
Vim 中如何去掉 ^M 字元?

TAG:Vim | vimrc | Vim插件 | vim腳本 |