【问题标题】:How to remove duplicate lines如何删除重复的行
【发布时间】:2019-05-27 04:42:45
【问题描述】:

我正在尝试创建一个从文件中删除重复行的简单程序。但是,我被困住了。我的目标是最终删除除 1 个重复行之外的所有重复行,与建议的重复行不同。所以,我还有那个数据。我也想这样做,它接受相同的文件名并输出相同的文件名。当我尝试使文件名相同时,它只会输出一个空文件。

input_file = "input.txt"
output_file = "input.txt"

seen_lines = set()
outfile = open(output_file, "w")

for line in open(input_file, "r"):
    if line not in seen_lines:
        outfile.write(line)
        seen_lines.add(line)

outfile.close()

输入.txt

I really love christmas
Keep the change ya filthy animal
Pizza is my fav food
Keep the change ya filthy animal
Did someone say peanut butter?
Did someone say peanut butter?
Keep the change ya filthy animal

预期输出

I really love christmas
Keep the change ya filthy animal
Pizza is my fav food
Did someone say peanut butter?

【问题讨论】:

  • 您打开文件两次,因为input_fileoutput_file 是相同的。第二次打开为已读,我认为这是您的问题所在。所以你将无法写作。
  • @busybear 是的。以r+ 身份打开您的文件以同时读取和写入文件(它们都可以工作)。

标签: python text-files


【解决方案1】:

只要我的两分钱,以防你碰巧能够使用 Python3。它使用:

  • 一个可重用的Path 对象,它有一个方便的write_text() 方法。
  • OrderedDict 作为数据结构,同时满足唯一性和顺序的约束。
  • 一个生成器表达式而不是 Path.read_text() 以节省内存。

# in-place removal of duplicate lines, while remaining order
import os
from collections import OrderedDict
from pathlib import Path

filepath = Path("./duplicates.txt")

with filepath.open() as _file:
    no_duplicates = OrderedDict.fromkeys(line.rstrip('\n') for line in _file)

filepath.write_text("\n".join(no_duplicates))

【讨论】:

    【解决方案2】:
    import os
    seen_lines = []
    
    with open('input.txt','r') as infile:
        lines=infile.readlines()
        for line in lines:
            line_stripped=line.strip()
            if line_stripped not in seen_lines:
                seen_lines.append(line_stripped)
    
    with open('input.txt','w') as outfile:
        for line in seen_lines:
            outfile.write(line)
            if line != seen_lines[-1]:
                outfile.write(os.linesep)
    

    输出:

    I really love christmas
    Keep the change ya filthy animal
    Pizza is my fav food
    Did someone say peanut butter?
    

    【讨论】:

    • 这解决了这个问题,对于小输入文件来说是一个很好的解决方案,但请注意,由于通过seen_lines 进行线性搜索,对于大文件来说它会很慢(二次时间)。
    • 当我使用这段代码时,我在输出中看到了两次Keep the change ya filthy animal
    • @Mark 我测试了代码,但没有看到。您可以照原样复制代码并重试吗?可能是您在输入时犯了一些无意的错误。
    • 等等,我认为这是因为最后一行在行尾有EOF,所以它认为它不是重复的。我测试了它。如果最后一行是重复行,由于EOF,它始终保留它。有什么办法吗?顺便说一句,我在窗户上
    • @Mark stackoverflow.com/questions/18857352/… 可能会有所帮助。我不能肯定地说。我在 Ubuntu 上。
    【解决方案3】:

    无论你做什么,outfile = open(output_file, "w") 行都会截断你的文件。随后的读取将找到一个空文件。我建议安全地执行此操作是使用临时文件:

    1. 打开一个临时文件进行写入
    2. 处理输入到新的输出
    3. 关闭这两个文件
    4. 将临时文件移至输入文件名

    这比打开文件两次读写要强大得多。如果出现任何问题,您将把原件和迄今为止所做的任何工作都藏起来。如果过程中出现任何问题,您当前的方法可能会弄乱您的文件。

    这是一个使用tempfile.NamedTemporaryFilewith 块的示例,以确保所有内容都正确关闭,即使出现错误:

    from tempfile import NamedTemporaryFile
    from shutil import move
    
    input_file = "input.txt"
    output_file = "input.txt"
    
    seen_lines = set()
    
    with NamedTemporaryFile('w', delete=False) as output, open(input_file) as input:
        for line in open(input_file, "r"):
            sline = line.rstrip('\n')
            if sline not in seen_lines:
                output.write(line)
                seen_lines.add(sline)
    move(output.name, output_file)
    

    即使输入和输出名称相同,最后的move 也能正常工作,因为output.name 保证与两者不同。

    还要注意,我正在从集合中的每一行中删除换行符,因为最后一行可能没有换行符。

    替代解决方案

    如果您不关心行的顺序,您可以通过直接在内存中执行所有操作来简化流程:

    input_file = "input.txt"
    output_file = "input.txt"
    
    with open(input_file) as input:
        unique = set(line.rstrip('\n') for line in input)
    with open(output_file, 'w') as output:
        for line in unique:
            output.write(line)
            output.write('\n')
    

    你可以对比一下

    with open(input_file) as input:
        unique = set(line.rstrip('\n') for line in input.readlines())
    with open(output_file, 'w') as output:
        output.write('\n'.join(unique))
    

    第二个版本完全一样,但是一次加载和写入。

    【讨论】:

    • 只是一个问题,如果超过 100,000 行,这种删除重复项的方法非常慢。有没有更好的办法?仍然出现同样的错误。
    • @马克。有了这个大小,您的 I/O 就是瓶颈。我怀疑你可以做很多事情来加快速度。
    • @马克。我提出了一个替代方案
    • 文件中的行已经按照我想要的顺序排列,您的第二个版本会删除重复项。如果有 2 个重复项,则最上面的重复项就是不应该删除的那个。
    • @马克。第二个版本将不会保留行的原始顺序。
    【解决方案4】:

    试试下面的代码,使用str.joinsetsorted的列表理解:

    input_file = "input.txt"
    output_file = "input.txt"
    seen_lines = []
    outfile = open(output_file, "w")
    infile = open(input_file, "r")
    l = [i.rstrip() for i in infile.readlines()]
    outfile.write('\n'.join(sorted(set(l,key=l.index))))
    outfile.close()
    

    【讨论】:

      【解决方案5】:

      我相信这是做你想做的最简单的方法:

      with open('FileName.txt', 'r+') as i:
          AllLines = i.readlines()
          for line in AllLines:
              #write to file
      

      【讨论】:

      • 那时重新打开写作会简单得多。如果要删除行,文件中会留下一条尾巴。
      【解决方案6】:

      问题是您正在尝试写入您正在读取的同一个文件。您至少有两种选择:

      选项 1

      使用不同的文件名(例如 input.txtoutput.txt)。这在某种程度上是最简单的。

      选项 2

      从输入文件中读取所有数据,关闭该文件,然后打开该文件进行写入。

      with open('input.txt', 'r') as f:
          lines = f.readlines()
      
      seen_lines = set()
      with open('input.txt', 'w') as f:
          for line in lines:
              if line not in seen_lines:
                  seen_lines.add(line)
                  f.write(line)
      

      选项 3

      使用r+ 模式打开文件进行读写。在这种情况下,您需要小心在写入之前读取要处理的数据。如果你在一个循环中做所有事情,循环迭代器可能会丢失。

      【讨论】:

        猜你喜欢
        • 2010-09-06
        • 2021-05-22
        • 1970-01-01
        • 1970-01-01
        • 2017-11-22
        • 2018-03-21
        • 2011-03-21
        • 2018-01-22
        相关资源
        最近更新 更多