Как удалить дубликаты строк

Я пытаюсь создать простую программу, которая удаляет дубликаты строк из файла. Тем не менее, я застрял. Моя цель состоит в том, чтобы в конечном итоге удалить все, кроме 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()

input.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?

Ответ 1

Строка outfile = open(output_file, "w") ваш файл, что бы вы ни делали. Последующие чтения найдут пустой файл. Моя рекомендация сделать это безопасно - использовать временный файл:

  1. Откройте временный файл для записи
  2. Обработка ввода в новый вывод
  3. Закройте оба файла
  4. Переместить временный файл к имени входного файла

Это гораздо надежнее, чем дважды открывать файл для чтения и записи. Если что-то пойдет не так, у вас будет оригинал и все, что вы делали до сих пор. Ваш текущий подход может испортить ваш файл, если что-то пойдет не так в процессе.

Вот пример использования tempfile.NamedTemporaryFile и блока with чтобы убедиться, что все закрыто правильно, даже в случае ошибки:

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 гарантированно будет отличаться от обоих.

Также обратите внимание, что я убираю новую строку из каждой строки в наборе, так как последняя строка может не иметь ее.

Alt Solution

Если вам не важен порядок строк, вы можете несколько упростить процесс, выполнив все прямо в памяти:

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))

Вторая версия делает то же самое, но загружает и записывает все сразу.

Ответ 2

Проблема в том, что вы пытаетесь записать в тот же файл, из которого читаете. У вас есть как минимум два варианта:

Опция 1

Используйте разные имена файлов (например, input.txt и output.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+. В этом случае вы должны быть осторожны, чтобы прочитать данные, которые вы собираетесь обработать, прежде чем писать. Если вы делаете все в одном цикле, итератор цикла может потерять след.

Ответ 3

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?

Ответ 4

Я считаю, что это самый простой способ сделать то, что вы хотите:

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

Ответ 5

Попробуйте приведенный ниже код, используя понимание списка с str.join set и sorted:

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()

Ответ 6

Только мои два цента, на случай, если вы сможете использовать Python3. Оно использует:

  • write_text() используемый объект 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))