Supprimer une ligne d'un fichier texte

Le problème exposé dans ce sujet a été résolu.

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.

+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

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.

+1 -0

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

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]
+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é !

+0 -0

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.

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

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 ?

+0 -0

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

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)

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 :

+0 -0

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)
+1 -0

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.

+3 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

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