web-dev-qa-db-fra.com

Comment modifier un fichier texte?

J'utilise Python et j'aimerais insérer une chaîne dans un fichier texte sans le supprimer ni le copier. Comment puis je faire ça?

159
Oscar

Malheureusement, il est impossible d'insérer au milieu d'un fichier sans le réécrire. Comme les précédentes affiches l'ont indiqué, vous pouvez ajouter à un fichier ou écraser une partie de celui-ci en utilisant search, mais si vous souhaitez ajouter des éléments au début ou au milieu, vous devrez les réécrire.

Ceci est une chose du système d'exploitation, pas une chose Python. C'est pareil dans toutes les langues.

Ce que je fais habituellement, c’est lire dans le fichier, apporter les modifications et l’écrire dans un nouveau fichier appelé myfile.txt.tmp ou quelque chose du genre. Cela vaut mieux que de lire le fichier entier en mémoire car le fichier est peut-être trop volumineux pour cela. Une fois le fichier temporaire terminé, je le renomme de la même manière que le fichier d'origine.

C’est un bon moyen de le faire, car si le fichier s’effondre ou s’arrête, quelle que soit la raison, votre fichier original n’a pas été modifié.

131
Adam Pierce

Tout dépends de ce que tu veux faire. Pour l'ajouter, vous pouvez l'ouvrir avec "a":

 with open("foo.txt", "a") as f:
     f.write("new line\n")

Si vous voulez ajouter quelque chose, vous devez d'abord lire le fichier:

with open("foo.txt", "r+") as f:
     old = f.read() # read everything in the file
     f.seek(0) # rewind
     f.write("new line\n" + old) # write the new line before
94
Armin Ronacher

Le module fileinput de la bibliothèque standard Python réécrira un fichier inplace si vous utilisez inplace = 1 paramètre:

import sys
import fileinput

# replace all occurrences of 'sit' with 'SIT' and insert a line after the 5th
for i, line in enumerate(fileinput.input('lorem_ipsum.txt', inplace=1)):
    sys.stdout.write(line.replace('sit', 'SIT'))  # replace 'sit' and write
    if i == 4: sys.stdout.write('\n')  # write a blank line after the 5th line
66
Dave

La réécriture d'un fichier sur place est souvent effectuée en sauvegardant l'ancienne copie sous un nom modifié. Les gens Unix ajoutent un ~ pour marquer l'ancien. Les utilisateurs de Windows font toutes sortes de choses - ajoutez .bak ou .old - ou renommez complètement le fichier ou placez le ~ devant le nom.

import shutil
shutil.move( afile, afile+"~" )

destination= open( aFile, "w" )
source= open( aFile+"~", "r" )
for line in source:
    destination.write( line )
    if <some condition>:
        destination.write( >some additional line> + "\n" )
source.close()
destination.close()

Au lieu de shutil, vous pouvez utiliser ce qui suit.

import os
os.rename( aFile, aFile+"~" )
31
S.Lott

Le module mmap de Python vous permettra de l'insérer dans un fichier. L'exemple suivant montre comment cela peut être fait sous Unix (Windows mmap peut être différent). Notez que cela ne gère pas toutes les conditions d'erreur et que vous risquez de corrompre ou de perdre le fichier d'origine. En outre, cela ne gérera pas les chaînes unicode.

import os
from mmap import mmap

def insert(filename, str, pos):
    if len(str) < 1:
        # nothing to insert
        return

    f = open(filename, 'r+')
    m = mmap(f.fileno(), os.path.getsize(filename))
    origSize = m.size()

    # or this could be an error
    if pos > origSize:
        pos = origSize
    Elif pos < 0:
        pos = 0

    m.resize(origSize + len(str))
    m[pos+len(str):] = m[pos:origSize]
    m[pos:pos+len(str)] = str
    m.close()
    f.close()

Il est également possible de le faire sans mmap avec les fichiers ouverts en mode 'r +', mais cela est moins pratique et moins efficace, car vous devez lire et stocker temporairement le contenu du fichier de la position d'insertion à EOF - qui pourrait être énorme.

14
mhawke

Comme mentionné par Adam, vous devez prendre en compte les limites de votre système avant de pouvoir décider si vous avez assez de mémoire pour tout lire en mémoire, remplacez-en des parties et ré-écrivez-en.

Si vous traitez avec un petit fichier ou n’avez pas de problèmes de mémoire, ceci pourrait aider:

Option 1) Lit le fichier entier en mémoire, substitue une expression rationnelle à la totalité ou à une partie de la ligne et remplace-le par cette ligne plus la ligne supplémentaire. Vous devrez vous assurer que la "ligne médiane" est unique dans le fichier ou que si vous avez des horodatages sur chaque ligne, cela devrait être assez fiable.

# open file with r+b (allow write and binary mode)
f = open("file.log", 'r+b')   
# read entire content of file into memory
f_content = f.read()
# basically match middle line and replace it with itself and the extra line
f_content = re.sub(r'(middle line)', r'\1\nnew line', f_content)
# return pointer to top of file so we can re-write the content with replaced string
f.seek(0)
# clear file content 
f.truncate()
# re-write the content with the updated content
f.write(f_content)
# close file
f.close()

Option 2) Déterminez la ligne médiane et remplacez-la par cette ligne plus la ligne supplémentaire.

# open file with r+b (allow write and binary mode)
f = open("file.log" , 'r+b')   
# get array of lines
f_content = f.readlines()
# get middle line
middle_line = len(f_content)/2
# overwrite middle line
f_content[middle_line] += "\nnew line"
# return pointer to top of file so we can re-write the content with replaced string
f.seek(0)
# clear file content 
f.truncate()
# re-write the content with the updated content
f.write(''.join(f_content))
# close file
f.close()
13
Maxime R.

A écrit une petite classe pour le faire proprement.

import tempfile

class FileModifierError(Exception):
    pass

class FileModifier(object):

    def __init__(self, fname):
        self.__write_dict = {}
        self.__filename = fname
        self.__tempfile = tempfile.TemporaryFile()
        with open(fname, 'rb') as fp:
            for line in fp:
                self.__tempfile.write(line)
        self.__tempfile.seek(0)

    def write(self, s, line_number = 'END'):
        if line_number != 'END' and not isinstance(line_number, (int, float)):
            raise FileModifierError("Line number %s is not a valid number" % line_number)
        try:
            self.__write_dict[line_number].append(s)
        except KeyError:
            self.__write_dict[line_number] = [s]

    def writeline(self, s, line_number = 'END'):
        self.write('%s\n' % s, line_number)

    def writelines(self, s, line_number = 'END'):
        for ln in s:
            self.writeline(s, line_number)

    def __popline(self, index, fp):
        try:
            ilines = self.__write_dict.pop(index)
            for line in ilines:
                fp.write(line)
        except KeyError:
            pass

    def close(self):
        self.__exit__(None, None, None)

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        with open(self.__filename,'w') as fp:
            for index, line in enumerate(self.__tempfile.readlines()):
                self.__popline(index, fp)
                fp.write(line)
            for index in sorted(self.__write_dict):
                for line in self.__write_dict[index]:
                    fp.write(line)
        self.__tempfile.close()

Ensuite, vous pouvez l'utiliser de cette façon:

with FileModifier(filename) as fp:
    fp.writeline("String 1", 0)
    fp.writeline("String 2", 20)
    fp.writeline("String 3")  # To write at the end of the file
1
ananth krishnan

Si vous connaissez un unix, vous pouvez essayer ce qui suit:

Notes: $ signifie l'invite de commande

Disons que vous avez un fichier my_data.txt avec un contenu en tant que tel:

$ cat my_data.txt
This is a data file
with all of my data in it.

Ensuite, en utilisant le module os, vous pouvez utiliser les commandes habituelles sed

import os

# Identifiers used are:
my_data_file = "my_data.txt"
command = "sed -i 's/all/none/' my_data.txt"

# Execute the command
os.system(command)

Si vous n'êtes pas au courant de sed, jetez-y un coup d'œil, c'est extrêmement utile.

0
G. LC