2011-09-29 16 views
4

我有這樣一個日誌文件:Vim的正則表達式重複的行分組

12 adsflljl 
12 hgfahld 
12 ash;al 
13 a;jfda 
13 asldfj 
15 ;aljdf 
16 a;dlfj 
19 adads 
19 adfasf 
20 aaaadsf 

,我謹以「組」他們喜歡這兩個中的一個:

12 adsfllj, 12 hgfahld, 12 ash;al 
13 a;jfda, 13 asldfj 
15 ;aljdf 
16 a;dlfj 
19 adads, 19 adfasf 
20 aaaadsf 

或者

12 adsfllj, hgfahld, ash;al 
13 a;jfda, asldfj 
15 ;aljdf 
16 a;dlfj 
19 adads, adfasf 
20 aaaadsf 

而我完全卡住了。如果vim沒有這樣做,我也有sed,awk和bash。我真的不想寫一個bash腳本,我想提高我的正則表達式福

回答

5

我只是用awk:

awk ' 
    { 
    sep = val[$1] ? ", " : "" 
    val[$1] = val[$1] sep $2 
    } 
    END {for (v in val) print v, val[v]} 
' log.file | sort > new.file 
+0

我沒有最微弱的線索是如何工作的,但它確實如此,所以這是我打開awk的教程以瞭解原因的一個很好的理由。好樣的!我的福已準備好增加! – Steve

+1

awk的一個奇怪特性是它如何進行字符串連接:沒有concat操作符(如'+'或'.') - 並排放置兩個變量,並將這些值連接在一起。運營商(比如上面的'?:')通常像C運營商一樣工作。另外,未設置變量的行爲類似於空字符串或數字爲零,因此是錯誤的。 –

6

在Vim中你可以使用:

:%s/\(\(\d\+\) .*\)\n\2/\1, \2/g 

這意味着:如果一組數字在新行後匹配,請刪除換行符並替換逗號。如果你不熟悉它們,\1\2是反向引用。

不幸的是,這種方法一次只能合併兩個事件,因此在實現目標之前必須多次運行它。

編輯:在單一行程中執行此操作的一種方法是循環並利用事實,即只要文件不匹配,就會發出錯誤。該錯誤是有點討厭,雖然,但我不能用一個班輪做的更好:

:while 1 | :%s/\(\(\d\+\) .*\)\n\2/\1, \2/g | :endwhile 
+0

感謝這也是偉大的,我給你點積分,他只是第一... – Steve

+0

...不用擔心:) – UncleZeiv

+0

+1,漂亮的vim回答 –

0

我不認爲這是在這裏使用正則表達式是個好主意。您可以以書面Vimscript中@glenn傑克曼的解決方案找到同樣的想法將是以下幾點:

function JoinLog() 
    let d={} 
    g/\v^\S+\s/let [ds, k, t; dl]=matchlist(getline('.'), '\v^(\S+)\s+(.*)') | 
       \let d[k]=get(d, k, [])+[t] 
    %delete _ 
    call setline(1, map(sort(keys(d)), 'v:val." ".join(d[v:val], ", ")')) 
endfunction 

你可以保持秩序,而不是排序:

function JoinLog() 
    let d={} 
    let ordered=[] 
    g/\v^\S+\s/let [ds, k, t; dl]=matchlist(getline('.'), '\v^(\S+)\s+(.*)') | 
       \if has_key(d, k) | let d[k]+=[t] | 
       \else    | let ordered+=[k] | let d[k]=[t] | 
       \endif 
    %delete _ 
    call setline(1, map(copy(ordered), 'v:val." ".join(d[v:val], ", ")')) 
endfunction 
2

在Vim中,我會用命令

:g/^\d\+/y|[email protected]"==getline(line('.')-1)|s//,/|-j! 

如果確保第一列始終包含數字標識。

否則,我會修改if-condition如下。

:g/^\S\+/y|if matchstr(@",@/)==matchstr(getline(line('.')-1),@/)|s//,/|-j! 
+2

+1可愛的轉換技巧 –

+0

@Peter:它也表現得很好:這兩個命令比UncleZeiv的替換循環或Benoit的命令序列快得多,並且快速(甚至更快一點比)ZyX的腳本。 –

1

另一種方式來做到這一點,與宏這次(我建議你使用另一種解決方案,這其中只是表明,有很多方法可以做到這一點):

gg:%s/$/,進入qa0V?CTRL-RCTRL-W\>\&^進入回報

解釋:

  • gg =>去啓動文件
  • :%s/$/, =>附加逗號每一行
  • qa =>開始錄製宏到寄存器a
  • 0V =>轉到第一欄,並開始逐行選擇
  • ? =>查找向後(必須具有set wrapscan
    • CTRL-R下光標CTRL-W插入字。
    • \>確保單詞結束
    • \&^確保模式匹配在行首。你不能把^在模式的開頭,因爲如果incsearch被設置,那麼只要你鍵入^然後CTRL-RCTRL-W將打印在光標的話,這將已經移動到前面線。
  • J將加入視覺選擇與空間的所有行。
  • j將轉到下一行
  • q將停止錄製宏
  • [email protected]會起到宏觀的100倍。
  • :%s/.$//將刪除尾隨逗號。
+0

@ib。感謝您的編輯 – Benoit