4

我有一个包含以下行的文件:

one one
one one
two two two
one one
three three
one one
three three
four

我想从文件中删除所有出现的重复行,只留下非重复行。因此,在上面的示例中,结果应该是:

two two two
four

我看到了一个类似问题的答案。我试图修改 ex one-liner,如下所示:

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

但它不会删除所有出现的重复行,它只会删除一些出现。

我怎样才能在 vim 中做到这一点?或者特别是如何在 vim 中使用 ex 执行此操作?

为了澄清,我不是在寻找sort u.

4

9 回答 9

5

如果您可以访问 UNIX 风格的命令,您可以执行以下操作:

:%!sort | uniq -u

该命令的-u选项uniq执行您需要的任务。从uniq命令的帮助文本中:

   -u, --unique
          only print unique lines

但是,我应该注意,此答案假定您不介意输出与输入文件可能已经具有的任何排序顺序不匹配。

于 2014-03-05T09:43:04.243 回答
3

如果您使用 awk 可用的 linux 机器,则此行可满足您的需求:

:%!awk '{a[$0]++}END{for(x in a)if(a[x]==1)print x}'
于 2014-03-05T09:38:08.520 回答
2

假设您使用的是 UNIX 衍生产品,下面的命令应该可以满足您的需求:

:sort | %!uniq -u

uniq只适用于已排序的行,所以我们必须先用 Vim 的内置:sort命令对它们进行排序以节省一些输入(默认情况下它适用于整个缓冲区,所以我们不需要传递一个范围,它是一个内置命令,所以我们不需要!)。

然后我们通过过滤整个缓冲区uniq -u

于 2014-03-05T09:45:40.147 回答
1

它不保留剩余行的顺序,但这似乎有效:

:sort|%s/^\(.*\)\n\%(\1\n\)\+//

(这个版本是@Peter Rincker 的想法,我做了一点修正。)在 vim 7.3 上,以下更短的版本可以工作:

:sort | %s/^\(.*\n\)\1\+//

不幸的是,由于正则表达式引擎之间的差异,这不再适用于 vim 7.4(包括补丁 1-52)。

于 2014-03-05T13:41:22.890 回答
1

我的PatternsOnText 插件版本 1.30 现在有一个

:DeleteAllDuplicateLinesIgnoring

命令。没有任何论据,它将按照您的问题中概述的方式工作。

于 2014-03-13T11:23:32.527 回答
0

这并不比@Ingo Karkat 的答案简单,但它更灵活一些。像那个答案一样,这会使剩余的行保持原始顺序。

function! RepeatedLines(...)
  let first = a:0 ? a:1 : 1
  let last = (a:0 > 1) ? a:2 : line('$')
  let lines = []
  for line in range(first, last - 1)
    if index(lines, line) != -1
      continue
    endif
    let newlines = []
    let text = escape(getline(line), '\')
    execute 'silent' (line + 1) ',' last
      \ 'g/\V' . text . '/call add(newlines, line("."))'
    if !empty(newlines)
      call add(lines, line)
      call extend(lines, newlines)
    endif
  endfor
  return sort(lines)
endfun
:for x in reverse(RepeatedLines()) | execute x 'd' | endfor

几点注意事项:

  1. 我的函数接受参数而不是处理范围。它默认为整个缓冲区。
  2. 这说明了一些用于操作列表的功能。 :help list-functions
  3. 我使用/\V(非常没有魔法)所以我需要在搜索模式中转义的唯一字符是反斜杠本身。 :help /\V
于 2014-03-05T15:28:16.507 回答
0

此处获取代码并对其进行修改以删除行而不是突出显示它们,您将得到:

function! DeleteDuplicateLines() range
  let lineCounts = {}
  let lineNum = a:firstline
  while lineNum <= a:lastline
    let lineText = getline(lineNum)
    if lineText != ""
        if has_key(lineCounts, lineText)
            execute lineNum . 'delete _'
            if lineCounts[lineText] > 0
              execute lineCounts[lineText] . 'delete _'
              let lineCounts[lineText] = 0
              let lineNum -= 1
            endif
        else
            let lineCounts[lineText] =  lineNum
            let lineNum += 1
        endif
    else
      let lineNum += 1
    endif
  endwhile
endfunction

command! -range=% DeleteDuplicateLines <line1>,<line2>call DeleteDuplicateLines()
于 2014-03-05T10:20:02.993 回答
0
  1. 添加行号,以便您可以恢复排序前的顺序 :%s/^/=printf("%d ", line("."))/g
  2. 排序:排序 /^\d+/
  3. 删除重复行:%s/^(\d+ )(.*)\n(\d+ \2\n)+//g
  4. 恢复顺序:排序
  5. 删除 #1 中添加的行号 :%s/^\d+ //g
于 2021-06-13T15:57:12.273 回答
-1

请使用perl,perl可以轻松搞定!

use strict;use warnings;use diagnostics;
#read input file
open(File1,'<input.txt') or die "can not open file:$!\n";my @data1=<File1>;close(File1);
#save row and count number of row in hash 
my %rownum;
foreach my $line1 (@data1)
{ 
    if (exists($rownum{$line1}))
    { 
        $rownum{$line1}++;
    }
    else
    {
        $rownum{$line1}=1;
    }
}
#if number of row in hash =1 print it
open(File2,'>output.txt') or die "can not open file:$!\n";
foreach my $line1 (@data1)
{ 
    if($rownum{$line1}==1)
    { 
        print File2 $line1;
    }
}
close(File2);
于 2014-04-30T06:52:14.567 回答