Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/310.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 从txt文件中删除重复的行_Python_Linux_Awk - Fatal编程技术网

Python 从txt文件中删除重复的行

Python 从txt文件中删除重复的行,python,linux,awk,Python,Linux,Awk,我正在处理大型文本文件(~20MB),其中包含以行分隔的数据。 大多数数据项都是重复的,我想删除这些重复项以只保留一个副本 另外,为了使问题稍微复杂一点,一些条目被重复,并附加了额外的信息位。在这种情况下,我需要保留包含额外信息的条目,并删除旧版本 e、 g。 我需要从这里开始: 鲍勃123 1DB 吉姆456 3DB AX 戴夫789 1DB 鲍勃123 1DB 吉姆456 3DB AX 戴夫789 1DB BOB 123 1DB额外位 为此: 吉姆456 3DB AX 戴夫789 1DB B

我正在处理大型文本文件(~20MB),其中包含以行分隔的数据。 大多数数据项都是重复的,我想删除这些重复项以只保留一个副本

另外,为了使问题稍微复杂一点,一些条目被重复,并附加了额外的信息位。在这种情况下,我需要保留包含额外信息的条目,并删除旧版本

e、 g。 我需要从这里开始: 鲍勃123 1DB 吉姆456 3DB AX 戴夫789 1DB 鲍勃123 1DB 吉姆456 3DB AX 戴夫789 1DB BOB 123 1DB额外位 为此: 吉姆456 3DB AX 戴夫789 1DB BOB 123 1DB额外位 注意。最后的顺序无关紧要

做这件事的有效方法是什么

我可以使用awk、python或任何标准的linux命令行工具

谢谢。

以下内容(Python)如何:

如果发现内存使用存在问题,可以使用Unix进行排序作为预处理步骤,然后更改脚本,使其不会将整个文件读入内存。

awk'{x[$1”“$2”“$3]=$0}END{for(y in x)print x[y]}'

如果需要指定不同文件的列数:

JIM 456 3DB AX DAVE 789 1DB BOB 123 1DB EXTRA BITS
awk-v ncols=3'
{
key=“”;
对于(i=1;i长度(x[key]){x[key]=0}
}
结束{for(y in x)打印y“\t”x[y]}
'

您必须定义一个函数,将行拆分为重要位和额外位,然后您可以执行以下操作:

prev = None
for line in sorted(open('file')):
  line = line.strip()
  if prev is not None and not line.startswith(prev):
    print prev
  prev = line
if prev is not None:
  print prev

由于您需要额外的位,最快的方法是创建一组唯一的条目(sort-u可以),然后您必须将每个条目相互比较,例如。
awk -v ncols=3 '
  {
    key = "";
    for (i=1; i<=ncols; i++) {key = key FS $i}
    if (length($0) > length(x[key])) {x[key] = $0}
  }
  END {for (y in x) print y "\t" x[y]}
'
如果x.startswith(y)而不是y.startswith(x)

如果您有perl并且只希望保留最后一个条目,那么只需保留x并放弃y即可。

def split_extra(s):
    """Return a pair, the important bits and the extra bits."""
    return blah blah blah

data = {}
for line in open('file'):
    impt, extra = split_extra(line)
    existing = data.setdefault(impt, extra)
    if len(extra) > len(existing):
        data[impt] = extra

out = open('newfile', 'w')
for impt, extra in data.iteritems():
    out.write(impt + extra)

此变体或轻微变体应能:

if x.startswith(y) and not y.startswith(x)
产出:

cat file.txt | perl -ne 'BEGIN{%k={}} @_ = split(/ /);$kw = shift(@_); $kws{$kw} = "@_"; END{ foreach(sort keys %kws){ print "$_ $kws{$_}";} }' > file.new.txt

函数
find_unique_line
适用于文件对象或字符串列表

finalData = {}
for line in input:
    parts = line.split()
    key,extra = tuple(parts[0:3]),parts[3:]
    if key not in finalData or extra:
        finalData[key] = extra

pprint(finalData)
BOB 123 1DB额外位 吉姆456 3DB AX
DAVE 789 1DBglenn jackman答案的这种变化应该适用,而不考虑带额外位的行的位置:

import itertools

def split_line(s):
    parts = s.strip().split(' ')
    return " ".join(parts[:3]), parts[3:], s

def find_unique_lines(f):
    result = {}
    for key, data, line in itertools.imap(split_line, f):
        if data or key not in result:
            result[key] = line
    return result.itervalues()

test = """BOB 123 1DB
JIM 456 3DB AX
DAVE 789 1DB
BOB 123 1DB
JIM 456 3DB AX
DAVE 789 1DB
BOB 123 1DB EXTRA BITS""".split('\n')

for line in find_unique_lines(test):
        print line

awk-v ncols=3'
{
key=“”;
对于(i=1;i长度(x[key])x[key]=0美元
}
结束{for(y in x)print x[y]}
'输入文件


快速而肮脏的版本可能是
cat file | sort | uniq
,但这无法处理带有“额外位”的行。它总是一个字和一个数字(可能还有一些添加的文本)?@Tim Pietzcker:遗憾的是,列数会随着条目的不同而变化(但通常是多个),额外位可以是一列或多列。我将修改我的示例以使其更清晰…消除重复行很容易。“sort-u”将自己完成这一切。但是您必须更好地定义部分匹配/子字符串。是否总是前两个字段会匹配,那么输出中的字段会比前两个字段多?同一个键会有多个额外的位值吗?缺点是,除非是最后一行的一部分匹配文件中的前3列,否则不会保留额外的位。
!我现在正在测试它,看看它是否完全符合我的需要,但是第一个结果看起来非常好。我很快会得到一个更明确的答案。不幸的是,正如Joe Kington提到的,当额外的位不是最新的条目时(我刚刚发现在某些情况下确实出现在我的txt文件中),这并不能完全解决我的问题。@Pete,如果你感兴趣的话,我添加了一个测试,以便保留具有最长额外位的行。我相信您缺少了一个括号。希望这是好的,我继续,并将其添加到。(虽然我认为(?)OP可能只想为(y in x)print x[y]
?这不是重点,尽管…)尽管我非常喜欢python,但令人遗憾的是awk最近经常被遗忘!)我稍微更改了你的代码–我使用了
str.startswith
而不是
len
进行切片。太好了-我现在只是测试一下,从我可以看出它正在工作。。。我将继续测试所有答案,并很快选择最佳解决方案。谢谢@皮特W:出于好奇,你们有没有用不同的解决方案做过基准测试?知道排序后的
对于20MB数据的成本有多高会很有趣。@shang:这似乎没有引起任何问题。这个解决方案在几分之一秒内返回了一个结果,这在本例中已经足够了。我还没有做过正式的基准测试,但它们似乎都能在一秒钟左右的时间内返回结果。将file.txt作为第一个参数传递给perlShouldn其实并不重要,我猜,谢谢!不幸的是,我不认为这适用于额外的位不是最后一个条目的情况(这可能发生,尽管我承认这在我的问题中并不明显)。@Pete W:是的。如果数据或键不在结果中,条件
将存储一行,如果1)它包含数据,或者2)如果前缀在字典中还不存在。它已经被测试了,中间也有额外的位。我很抱歉,你用一个字符串写的例子是有效的。我一直在用一个文件测试它(即用file=open('data.txt')替换test),它只是返回最近的一行(没有额外的位)。我试图用我在一个文件中提供的样本数据来重现这一点,但是用样本数据,我得到它为Bob返回了2行-有和没有额外的位。。。我糊涂了@皮特W:在什么地方会有额外的空白吗?我添加了一个额外的
split()
调用,该调用应该会处理这个问题。这并不重要,因为您已经从另一张海报中获得了一个有效的解决方案,但我只想知道错误在哪里,以便我可以从错误中吸取教训。:)我想这一定是一个空白的问题。您的最新解决方案在文件中的示例数据中对我有效。出于某种原因,当我在真实数据上使用它时,它仍然返回数据,而不使用“额外的”
{('BOB', '123', '1DB'): ['EXTRA', 'BITS'],
 ('DAVE', '789', '1DB'): [],
 ('JIM', '456', '3DB'): ['AX']}
import itertools

def split_line(s):
    parts = s.strip().split(' ')
    return " ".join(parts[:3]), parts[3:], s

def find_unique_lines(f):
    result = {}
    for key, data, line in itertools.imap(split_line, f):
        if data or key not in result:
            result[key] = line
    return result.itervalues()

test = """BOB 123 1DB
JIM 456 3DB AX
DAVE 789 1DB
BOB 123 1DB
JIM 456 3DB AX
DAVE 789 1DB
BOB 123 1DB EXTRA BITS""".split('\n')

for line in find_unique_lines(test):
        print line
BOB 123 1DB EXTRA BITS JIM 456 3DB AX DAVE 789 1DB
awk '{idx = $1 " " $2 " " $3; if (length($0) > length(x[idx])) x[idx] = $0} END {for (idx in x) print x[idx]}' inputfile
awk -v ncols=3 '
  {
    key = "";
    for (i=1; i<=ncols; i++) {key = key FS $i}
    if (length($0) > length(x[key])) x[key] = $0
  }
  END {for (y in x) print x[y]}
' inputfile