Supprimer une ligne d'un fichier texte

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour, Je voulais savoir s'il y avait un moyen de supprimer une ligne dans un fichier texte sans avoir à passer par un fichier temporaire ou à devoir recopier toutes les autres lignes en gros en temps constant. Merci pour votre aide

+0 -0

Comment détermines-tu (reconnais-tu) cette ligne pour ensuite là supprimer ?

EDIT: Sur le principe, on récupère les lignes sous forme d'une liste de chaînes de caractères, on traite ces chaînes, puis on réécrit dans le fichier…

Édité par fred1599

+0 -0
Auteur du sujet

Je me suis mal exprimé. Je m'autorise une lecture en temps linéaire pour déterminer l'emplacement de la ligne avec readlines() et tout. Et ce qu'il y aurait un moyen un fois que l'on a repéré la ligne d'ouvrir le fichier en écriture et supprimer la ligne ?

+0 -0

Bah, non. Toutes les autres techniques sont potentiellement bien plus compliquées et de toute façon ne te feront pas gagner grand chose, le fichier doit quoi qu'il arrive être relu en totalité pour pouvoir être réenregistré sur le disque.

De toute façon le temps constant tu le passes déjà, en lisant le fichier et pour trouver la ligne à supprimer; alors un peu plus un peu moins.

Mais en python, lire un fichier ligne par ligne, en supprimer une et le réenregistrer, ce n'est vraiment pas compliqué…

1
2
3
4
5
lines = None
with file = open('truc.txt', 'r') :
  lines = [l in file.readlines() if l!='la ligne que tu veux pas']
with file = open('truc.txt', 'w') :
 file.write("\r\n".join(lines))

Peut-être même qu'il y a quelqu'un qui est capable de sortir un one-liner.

Ma plateforme avec 23 jeux de société classiques en 6 langues et 13000 joueurs: http://qcsalon.net/ | Apprenez à faire des sites web accessibles http://www.openweb.eu.org/

+0 -0

Le plus simple est d'utiliser sed pour ça, il est possible d'utiliser des expressions regulieres pour cibler la ligne voulue et même faire des remplacements. (attention avec l'option -i qui va écraser le fichier, mieux vaut tester d'abord que le script marche bien comme on veut).

Exemple pour supprimer les lignes contenant "cible" :

1
sed -i -e '/cible/ d' monfichier.txt
+1 -0

Oui, mais ça devrait exister sous tous les systèmes d'exploitation tellement c'est pratique …

Sinon, avec Python cette démarche reste la meilleur même si tu souhaites modifier.

Édité par ache

+1 -0

Cette réponse a aidé l'auteur du sujet

Avec un exemple simple, ça donne ceci

1
2
3
4
5
>>> lines = ['a', 'b', 'c', 'd', 'e']
>>> index = lines.index('c') # récupération de l'index de la ligne recherchée
>>> res = lines[0:index] + lines[index+1:]
>>> res # Lignes avec suppression de la ligne recherchée
['a', 'b', 'd', 'e']

Bonne continuation…

P.S Attention avec readlines qui retourne la chaîne + le caractère de fin de ligne '\n'.

+1 -0
Auteur du sujet

Ok merci ! J'avais remarqué pour readlines c'est vrai que c'est trompeur… Dernière question : y a-t-il un moyen de joindre des bouts de liste i.e. on a une liste d'index et on veut avoir l[i0:i1-1]+l[i1:i2-1]+etc… EDIT : je dois pouvoir m'en sortir comme ça :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
indexes = []
for ligne in lignes:
    if ligne.find("motif"):
        indexes.append(ligne.index)

l = []
if len(indexes)==0:
    l = lignes
else:
    for i in range(indexes)-1:
       l+=l[indexes[i]:indexes[i+1]-1]

Édité par victora914

+0 -0

Il faut faire des essais sur l'interpréteur, il y a plus simple pour tester qu'une sous-chaîne se trouve dans ta chaîne de base,

1
2
3
>>> line = "lfkljljfmotifcdkvmfkm"
>>> "motif" in line
True

Pour visualiser chaque ligne d'un fichier, sachant que c'est un itérateur, on peut directement faire

1
2
for line in f:
    # traitement de line

Le but serait d'éliminer toutes les lignes contenant "motif" ?

Si oui, on peut copier la liste en ne gardant que les lignes ne contenant pas "motif"

1
2
with open("....", 'r') as f:
    res = [line.rstrip('\n') for line in f if "motif" not in line]

Non testé !

Édité par fred1599

+0 -0
Auteur du sujet

Ah je ne savais pas que le fichier était directement itérable. Non en fait je vais ajouter n lignes dans mon fichier. Pour chacune des n lignes, si elle se trouve déjà dans le fichier je supprime celle du fichier et je la remplace par la même avec un 2 à la fin sinon je l'ajoute dans le fichier.

+0 -0

Ok, je pense avoir toutes les données dont tu as besoin, plus celles de mes VDD, tu peux faire tes choix et créer ton code, plus qu'à poster ta solution, et si besoin est, on t'aideras à corriger.

Bonne journée,

+0 -0
Auteur du sujet

Bonjour voici mon code. Il compile bien mais il laisse le fichier inchangé…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def updateGlobalBDD():
    global localBDD
    f = open('BDD.txt','r+')
    lignes  = f.readlines()
    for elem in localBDD:
        count = 0
        chaine = ""
        new=True
        for ligne in f:
            if ligne.find(elem)!=-1:
                new=False
                tmp = ligne
                posVal = tmp.find(";")
                dN = int(tmp[0:posVal])
                posEnd=tmp.find("\n")
                num = int(tmp[posVal+1:posEnd])
                num+=1
                chaine =str(dN)+";"+str(num)+"\n"
            if not new:
                f.writelines(lignes[0:count]+lignes[count+1:])
                f.write(chaine)
                localBDD.remove(elem)
                break
            count+=1

    for elem in localBDD:
            f.write(str(elem)+";1"+"\n")
    f.close()
+0 -0

Houlà, tu te mélanges un peu là :)

  1. Plus d'intérêt à readlines, si tu utilises for line in f: ou vice et versa
  2. Mieux vaut ouvrir comme spécifié plus haut avec with open(..., 'r') as ...: car l'avantage est de ne pas se soucier d'un éventuel oubli de la fermeture du fichier.
  3. L'algo de recherche semble bien trop complexe pour le peu à faire
  4. Le mot clé global me semble inutile si localBDD est une liste python

Sans tester, rapidement, ça devrait faire un truc comme ci-dessous,

1
2
3
4
5
6
with open('...', 'r') as f:
    lines = [line.rstrip('\n') for line in f]
    for line in lines:
        for elem in localBDD:
            if elem in line:
                # traitement de line

Mais là je me dis que l'on ne connaît pas toute l'histoire, car je pense qu'on peut faire plus simple.

Si tu posais le problème réel, avec une exemple de ligne, ta liste localBDD ou un exemple, et le résultat attendu ?

Édité par fred1599

+0 -0
Auteur du sujet

Bonjour. Merci pour ta réponse d'abord. Je pense que la partie qui semble compliquer c'est juste la séparation de la chaine qui est sous la forme "a;x" puis de modifier en "a;x+1". J'ai bien compris le point n°1 et le point n°2. Par rapport au point n°3, il me semble que mon algo est le même en terme de complexité… Et pour le point n°4 justement je me demandais. Dès lors que j'utilise la liste dans d'autres fonctions je n'ai pas besoin de la passer en global ?

Exemple de bdd.txt:

1
2
123423;1
456678;1
1
localBDD = ["567893","456678","121212"]

bdd.txt modifié :

1
2
3
4
123423;1
456678;2
567893;1
121212;1
+0 -0

Cette réponse a aidé l'auteur du sujet

On pourrait créer une fonction comme celle-ci, à tester si ça te convient

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import io

def increment(line, elements, delimiter=';'):

    if not elements:
        return

    n, inc = line.split(delimiter)
    inc = int(inc)

    if n not in elements:
        inc = 1
    else:
        inc += 1
        elements.remove(n)

    new_line = '{n};{inc}'.format(n=n, inc=inc)

    return new_line


localBDD = ["567893","456678","121212"]

myData = io.StringIO("123423;1\n456678;1") # pour simuler un fichier

for line in myData:
    new = increment(line, localBDD)
    print(new) # écrire dans le fichier

print('localBDD: ', localBDD)
+0 -0
Auteur du sujet

Génial c'est exactement ça. En plus c'est beaucoup plus concis comme code. J'ai plus qu'à écrire tous ceux dans la localBDD après dans le fichier (ceux qui sont nouveaux du coup) Merci beaucoup. Je suppose que maintenant j'ai juste à remplacer les stringIOpar des open() et le print par un write ? EDIT :

Édité par victora914

+0 -0

Cette réponse a aidé l'auteur du sujet

Oui c'est ça, j'ai un peu amélioré pour rendre plus visible la partie fichier

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import io

def increment(line, elements, delimiter=';'):

    if not elements:
        return

    n, inc = line.split(delimiter)
    inc = int(inc)

    if n in elements:
        inc += 1
        elements.remove(n)

    new_line = '{n};{inc}\n'.format(n=n, inc=inc)

    return new_line


localBDD = ["567893","456678","121212"]

myData = io.StringIO("123423;1\n456678;1") # pour simuler un fichier

lines = []
for line in myData:
    new = increment(line, localBDD)
    lines.append(new)

for data in localBDD:
    lines.append(data+';1\n')

myData.truncate(0) # suppression de l'ancien fichier
myData.writelines(lines) # écriture de l'ensemble des lignes
print(myData.getvalue()) # for line in f: print(line)

Édité par fred1599

+1 -0
Staff

Accessoirement il y a aussi moyen de faire la même chose sans stocker la totalité du fichier en mémoire :

1
2
3
4
5
6
7
infile = open('monfichier', 'r')
outfile = open('monfichier', 'w')
with infile, outfile: 
    for line in infile: 
        if MOTIF in line:
            continue
        outfile.write(line)

Sur des très gros fichiers, ça évite de saturer inutilement la mémoire.

Édité par nohar

I was a llama before it was cool

+3 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte