删除Vim中的重复行?

时间:2009-12-13 15:17:19

标签: xml regex vim

我正在尝试使用VIM删除我创建的XML文件中的重复行。 (我无法重新创建文件,因为ID号会改变。)

该文件看起来像这样:

    <tag k="natural" v="water"/>
    <tag k="nhd:fcode" v="39004"/>
    <tag k="natural" v="water"/>

我正在尝试删除其中一个重复的k =“natural”v =“water”行。当我尝试使用\_修饰符在我的正则表达式替换中包含换行符时,VIM似乎找不到任何内容。

有关正则表达式或工具使用的任何提示吗?

9 个答案:

答案 0 :(得分:4)

首先,您可以使用awk删除所有重复的行,并保留其顺序。

:%!awk '\!_[$0]++'

如果你不确定是否有其他重复行你不想删除,那么只需添加条件。

:%!awk '\!(_[$0]++ && /tag/ && /natural/ && /water/)'

但是,用正则表达式解析像xml这样的嵌套结构是一个坏主意,恕我直言。 你要关心他们不要一直搞砸。 xmllint为您提供文件中特定元素的列表:

:!echo "cat //tag[@k='natural' and @v='water']" | xmllint --shell %

您可以逐步删除重复的行。

答案 1 :(得分:1)

而不是使用vim,你会做类似

的事情
sort filename | uniq -c | grep -v "^[ \t]*1[ \t]"

找出什么是重复行,然后使用普通搜索来访问它并删除它

答案 2 :(得分:1)

使用'uniq'的答案会遇到'uniq'只找到相邻的重复行,或者数据文件被排序丢失位置信息的问题。

如果不能重复任何行,那么在Perl(或其他具有正则表达式和关联数组支持的脚本语言)中进行相对简单的操作,假设数据源不是非常令人难以置信:

#!/bin/perl -w
# BEWARE: untested code!
use strict;
my(%lines);
while (<>)
{
    print if !defined $lines{$_};
    $lines{$_} = 1;
}

但是,如果不加选择地使用它,这可能会破坏XML,因为结束标记是合法重复的。怎么避免这个?也许是通过“可以重复”行的白名单?或者,只有带有值的开放标记的行可能会重复消除:

#!/bin/perl -w
# BEWARE: untested code!
use strict;
my(%lines);
while (<>)
{
    if (m%^\s*<[^\s>]+\s[^\s>]+%)
    {
         print if !defined $lines{$_};
         $lines{$_} = 1;
    }
    else
    {
         print;
    }
}

当然,还有(很大程度上有效)的论点,即使用正则表达式处理XML是错误的。这种编码假设XML带有许多方便的换行符;真正的XML可能不包含任何内容,也可能只包含极少数。

答案 3 :(得分:1)

如果您不关心订购,可以选择行然后执行:'<,'>sort u。它将对重复项进行排序和删除。

答案 4 :(得分:1)

到OP,如果你有bash 4.0

#!/bin/bash
# use associative array
declare -A DUP
file="myfile.txt"
while read -r line
do
    if [ -z ${DUP[$line]} ];then
        DUP[$line]=1
        echo $line >temp
    fi
done < "$file"
mv temp "$file"

答案 5 :(得分:1)

使用python删除所有重复的行:

#!/usr/bin/env python

import sys
def remove_identical(filein, fileout) : 
  lines = list()
  for line in open(filein, 'r').readlines() : 
    if line not in lines : lines.append(line)
  fout = open(fileout, 'w')
  fout.write(''.join(lines))
  fout.close()

remove_identical(sys.argv[1], sys.argv[2])

答案 6 :(得分:1)

简单的正则表达式是不够的。我已经实现了

:DeleteDuplicateLinesIgnoring
我的PatternsOnText plugin中的

命令(以及相关命令)。您甚至可以提供{pattern}以从重复数据删除中排除某些行。

答案 7 :(得分:0)

您是否尝试搜索并更换线路?您可以尝试使用g命令:

:%g/search_expression_here/d

最后的d告诉它删除匹配的行。

您可以找到更多提示here

答案 8 :(得分:0)

似乎bash,python和perl方法可行但你已经在vim中了。那么为什么不创建一个像:

这样的函数
function! RemoveDuplicateLines()
    let lines={}
    let result=[]
    for lineno in range(line('$'))
        let line=getline(lineno+1)
        if (!has_key(lines, line))
            let lines[line] = 1
            let result += [ line ]
        endif
    endfor
    %d
    call append(0, result)
    d
endfunction