web-dev-qa-db-fra.com

Comment supprimer les lignes en double

J'essaie de créer un programme simple qui supprime les lignes en double d'un fichier. Cependant, je suis coincé. Mon objectif est de supprimer finalement toutes les lignes sauf 1 en double, différentes des lignes en double suggérées. Donc, j'ai toujours ces données. Je voudrais aussi le faire ainsi, il prend le même nom de fichier et sort le même nom de fichier. Lorsque j'ai essayé de faire en sorte que les noms de fichiers soient identiques, il ne génère qu'un fichier vide.

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

Production attendue

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

La ligne outfile = open(output_file, "w") tronque votre fichier, quoi que vous fassiez. Les lectures suivantes trouveront un fichier vide. Ma recommandation pour le faire en toute sécurité est d'utiliser un fichier temporaire:

  1. Ouvrir un fichier temporaire pour l'écriture
  2. Traiter l'entrée dans la nouvelle sortie
  3. Fermer les deux fichiers
  4. Déplacez le fichier temporaire vers le nom du fichier d'entrée

C'est beaucoup plus robuste que d'ouvrir deux fois le fichier pour la lecture et l'écriture. Si quelque chose ne va pas, vous aurez l'original et tout le travail que vous avez fait jusqu'ici caché. Votre approche actuelle peut gâcher votre fichier si quelque chose ne va pas dans le processus.

Voici un exemple utilisant tempfile.NamedTemporaryFile et un bloc with pour vous assurer que tout est correctement fermé, même en cas d'erreur:

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)

La move à la fin fonctionnera correctement même si les noms d'entrée et de sortie sont identiques, puisque output.name est garanti d'être différent des deux.

Notez également que je supprime la nouvelle ligne de chaque ligne du jeu, car la dernière ligne pourrait ne pas en avoir une.

Solution alternative

Si vous ne vous souciez pas de l'ordre des lignes, vous pouvez simplifier quelque peu le processus en faisant tout directement dans la mémoire:

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

Vous pouvez comparer cela contre

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

La deuxième version fait exactement la même chose, mais charge et écrit en même temps.

5
Mad Physicist

Le problème est que vous essayez d'écrire dans le même fichier que celui que vous lisez. Vous avez au moins deux options:

Option 1

Utilisez des noms de fichiers différents (par exemple, input.txt et output.txt ). C'est, à un certain niveau, le plus facile.

Option 2

Lisez toutes les données de votre fichier d’entrée, fermez ce fichier, puis ouvrez-le en écriture.

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)

Option 3

Ouvrez le fichier pour lire et écrire en utilisant le mode r+. Dans ce cas, vous devez faire attention à lire les données que vous allez traiter avant de les écrire. Si vous faites tout en une seule boucle, l'itérateur de boucle peut perdre la trace.

3
Jonah Bishop
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)

Sortie:

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

Essayez le code ci-dessous en utilisant la compréhension de liste avec str.join et set et 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()
0
U9-Forward

Je crois que c'est la meilleure façon de faire ce que vous voulez:

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

Juste mes deux sous, au cas où vous seriez capable d'utiliser Python3. Il utilise:

  • Un objet Path réutilisable qui possède une méthode pratique write_text().
  • OrderedDict comme structure de données pour satisfaire les contraintes d'unicité et d'ordre à la fois.
  • Une expression génératrice à la place de Path.read_text() pour économiser de la mémoire.

# 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))
0
timmwagener