如何在VI编辑器中标记/突出显示重复的行?


69

您将如何标记缓冲区中与其他行完全相同的所有行?通过标记它们,我的意思是突出显示它们或添加字符或其他内容。我想保留缓冲区中各行的顺序。

之前:

foo
bar
foo
baz

后:

foo*
bar
foo*
baz

Answers:


96

作为前班轮:

:syn clear Repeat | g/^\(.*\)\n\ze\%(.*\n\)*\1$/exe 'syn match Repeat "^' . escape(getline('.'), '".\^$*[]') . '$"' | nohlsearch

这将使用该Repeat组突出显示重复的行。

分解:

  • syn clear Repeat ::删除任何先前发现的重复
  • g/^\(.*\)\n\ze\%(.*\n\)*\1$/ :::文件中稍后重复的任何行
    • 正则表达式
      • ^\(.*\)\n ::整行
      • \ze ::比赛结束-验证模式的其余部分,但不要使用匹配的文本(正向超前)
      • \%(.*\n\)* ::任意数量的实线
      • \1$ ::匹配的完整行的完整行重复
    • exe 'syn match Repeat "^' . escape(getline('.'), '".\^$*[]') . '$"'::将与此匹配的完整行添加到Repeat语法组
      • exe ::将给定的字符串作为ex命令执行
      • getline('.') ::当前行内容匹配 g//
      • escape(..., '".\^$*[]') ::用反斜杠转义给定字符以使正则表达式合法
      • syn match Repeat "^...$"::将给定的字符串添加到Repeat语法组
  • nohlsearch ::从搜索结果中删除突出显示 g//

贾斯汀的非正则表达式方法可能更快:

function! HighlightRepeats() range
  let lineCounts = {}
  let lineNum = a:firstline
  while lineNum <= a:lastline
    let lineText = getline(lineNum)
    if lineText != ""
      let lineCounts[lineText] = (has_key(lineCounts, lineText) ? lineCounts[lineText] : 0) + 1
    endif
    let lineNum = lineNum + 1
  endwhile
  exe 'syn clear Repeat'
  for lineText in keys(lineCounts)
    if lineCounts[lineText] >= 2
      exe 'syn match Repeat "^' . escape(lineText, '".\^$*[]') . '$"'
    endif
  endfor
endfunction

command! -range=% HighlightRepeats <line1>,<line2>call HighlightRepeats()

2
非正则表达式方法快如闪电!非常好的脚本,谢谢!
Hassek 2014年

1
这真是太棒了!
pymarco 2014年

我无法使它正常工作。我已将函数放入我的〜/ .vimrc中,但是当我运行“:call HighlightRepeats()”时,我得到一个错误:处理函数HighlightRepeats时检测到错误:第10行:E28:没有这样的突出显示组名:Repeat
Motsel

Daps0l:尝试添加hi link Repeat Statement到您的中~/.vimrc
风铃草

1
打破它的荣誉!
Mihai AlexandruBîrsan2015年


14
  1. :sort并将其保存在中file1
  2. :sort u并将其保存在中file2
  3. gvimdifftkdiff两个文件。

3

为什么不使用:

V*

在正常模式下。

它只是搜索当前行的所有匹配项,从而突出显示它们(如果启用了设置,我认为这是默认设置),然后,您可以使用

n

浏览比赛


1
视觉模式默认情况下不支持*。这可能是您在.vimrc中具有的功能。诸如此类:xno *:<cu> cal <SID> VisualSearch()<cr> / <cr> xno#:<cu> cal <SID> VisualSearch()<cr>?<cr>好玩!s:VisualSearch()let old = @“ | norm!gvy let @ / ='\ V'.substitute(escape(@”,'\'),'\ n','\\ n','g') let @“ = old endf)
迈克尔,

Arg,格式搞乱了。这里就是我的意思:pastebin.com/f2ee37c92
迈克尔

一次只能匹配一件事,而我宁愿一次指出所有与其他行重复的行。功能不错,但似乎很方便。
Brian Carper

2

遍历列表一次,对每个字符串及其出现次数进行映射。再次循环遍历,并将*附加到映射中值大于1的任何字符串。


2

尝试:

:%s:^\(.\+\)\n\1:\1*\r\1:

希望这行得通。

更新:下次尝试。

:%s:^\(.\+\)$\(\_.\+\)^\1$:\1\r\2\r\1*:

这将仅检测相邻的重复行,并且仅标记第一个副本,而不标记第二个副本。
风铃草
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.