Vim Regex Duplicate Lines Grouping - PullRequest
       23

Vim Regex Duplicate Lines Grouping

4 голосов
/ 29 сентября 2011

У меня есть такой файл журнала:

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, я хочу увеличить свое регулярное выражение * fu

Ответы [ 5 ]

6 голосов
/ 29 сентября 2011

В Vim вы можете использовать:

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

, что означает: если группа чисел сопоставляется после новой строки, удалите новую строку и поместите вместо нее запятую. Если вы не знакомы с ними, \1 и \2 являются обратными ссылками.

К сожалению, это объединяет только два вхождения одновременно, поэтому вам придется запускать его несколько раз, прежде чем вы достигнете своей цели.

РЕДАКТИРОВАТЬ: один из способов сделать это за один раз - циклично использовать тот факт, что, как только файл больше не совпадает, выдается ошибка. Хотя эта ошибка немного раздражает, но я не могу добиться большего успеха с однострочником:

:while 1 | :%s/\(\(\d\+\) .*\)\n\2/\1, \2/g | :endwhile
5 голосов
/ 29 сентября 2011

Я бы просто использовал 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
2 голосов
/ 30 сентября 2011

В Vim я бы использовал команду

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

, если гарантируется, что первый столбец всегда содержит цифровые идентификаторы.

В противном случае я бы изменил это условие if следующим образом.

:g/^\S\+/y|if matchstr(@",@/)==matchstr(getline(line('.')-1),@/)|s//,/|-j!
1 голос
/ 30 сентября 2011

Еще один способ сделать это, с помощью макроса на этот раз (я советую вам использовать другое решение, это просто показывает, что существует множество способов сделать это):

gg:%s/$/, введите qa0V? Ctrl-R Ctrl-W \>\&^ введите Jjq100@a:%s/.$// вернуться

Объяснение:

  • gg => перейти к началу файла
  • :%s/$/, => добавить запятую к каждой строке
  • qa => начать запись макроса в регистр a
  • 0V => перейти к первому столбцу и начать выбор строки
  • ? => поиск назад (у вас должно быть set wrapscan)
    • ctrl-r ctrl-w вставляет слово под курсором.
    • \> обеспечивает конец слова
    • \&^ обеспечивает сопоставление с образцом в начале строки. Вы не можете поставить ^ в начале шаблона, потому что если установлено incsearch, то, как только вы наберете ^, тогда ctrl-r ctrl-w напечатает слово под курсором, которое переместится на предыдущую строку.
  • J объединит все строки визуального выделения с пробелами.
  • j перейдет на следующую строку
  • q остановит запись макроса
  • 100@a будет воспроизводить макрос 100 раз.
  • :%s/.$// удалит запятые.
0 голосов
/ 29 сентября 2011

Я не думаю, что это хорошая идея использовать здесь регулярные выражения. Идея, которую вы можете найти в решении @glenn jackman, написанном на vimscript, будет следующей:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...