Come modificare un file di testo?


175

Sto usando Python e vorrei inserire una stringa in un file di testo senza eliminare o copiare il file. Come lo posso fare?


1
Puoi fare riferimento a questa risposta di Alex Martelli.
Alok



@Ani l'altro post è un duplicato di Inserimento linea nella posizione specificata di un file di testo e sicuramente ci sono chiare risposte composte qui, perché non aggiungere la tua risposta qui invece dell'altro modo? La risposta accettata non è un requisito per una buona domanda.
Bhargav Rao

@BhargavRao Voto ritirato. Avrei dovuto trovare quel duplicato però!
Ani Menon,

Risposte:


134

Sfortunatamente non c'è modo di inserirlo nel mezzo di un file senza riscriverlo. Come hanno indicato i poster precedenti, puoi aggiungere a un file o sovrascriverne una parte usando seek ma se vuoi aggiungere materiale all'inizio o al centro, dovrai riscriverlo.

Questa è una cosa del sistema operativo, non una cosa di Python. È lo stesso in tutte le lingue.

Quello che faccio di solito è leggere dal file, apportare le modifiche e scriverlo in un nuovo file chiamato myfile.txt.tmp o qualcosa del genere. È meglio che leggere l'intero file in memoria perché il file potrebbe essere troppo grande per quello. Una volta completato il file temporaneo, lo rinomino come il file originale.

Questo è un buon modo sicuro per farlo perché se la scrittura del file si arresta in modo anomalo o si interrompe per qualsiasi motivo, hai ancora il tuo file originale intatto.


3
Gli strumenti unix come awk / sed fanno qualcosa di simile nel loro codice?
Manish Gill,

Non è vero che questo è lo stesso in tutte le lingue. In ActionScript: fileStream.openAsync (nome file, FileMode.UPDATE); Quindi posso andare ovunque nel file che voglio e cambiare qualsiasi cosa.
AndrewBenjamin,

2
@AndrewBenjamin Sai cosa chiama il sistema ActionScript? Esiste la possibilità che openAsync legga il file e ne scriva uno nuovo dopo la chiamata?
AlexLordThorsen,

@Rawrgulmuffins Io no. Tuttavia, so che non sta leggendo l'intero file in memoria, poiché l'ho usato per gestire dimensioni di file di diversi GB. Sospetto che sia lo stesso che scrivere con streamwriter C #. Vedo python come uno strumento per fare rapidamente piccole cose, piuttosto che lo sviluppo su larga scala e la manipolazione dei file.
AndrewBenjamin,

4
@AndrewBenjamin, l'utente non sta chiedendo di cercare nel file e cambiarlo (ogni lingua che conosco può farlo); sta chiedendo di inserire del testo, che è diverso dal semplice cambiare / sovrascrivere ciò che è già nel file. Forse in un'applicazione pratica è diverso, ma nulla che io possa trovare nell'API di ActionScript indica che si comporta in modo diverso da qualsiasi altra lingua in questo senso.
eestrada,

104

Dipende da cosa vuoi fare. Per aggiungere puoi aprirlo con "a":

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

Se vuoi preprendere qualcosa, devi prima leggere dal file:

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

9
Solo una piccola aggiunta, per usare l' withistruzione in Python 2.5 è necessario aggiungere "dall'importazione futura with_statement". Oltre a ciò, l'apertura dei file con l' withistruzione è sicuramente più leggibile e meno soggetta a errori rispetto alla chiusura manuale.
Alexander Kojevnikov,

2
Potresti considerare la fileinputlibreria helper con le operazioni di apertura / lettura / modifica / scrittura / sostituzione sporche ben gestite quando usi inline=Truearg. Esempio qui: stackoverflow.com/a/2363893/47390
mikegreenberg

3
Basta non dimenticare di chiudere il file. f.Close()
D.Rosado,

5
Non è uno stile che uso, D.Rosado, ma quando si utilizza lo stile with, non penso che sia necessario chiudere manualmente. Il with tiene traccia della risorsa che crea.
Chris,

4
Non è necessario chiudere manualmente il file. Questo è il punto centrale dell'uso "con" qui. (Beh, in realtà, Python lo fa non appena l'oggetto file viene garbage collection, cosa che in CPython si verifica quando il nome associato ad esso esce dall'ambito ... ma altre implementazioni no, e CPython potrebbe smettere di farlo un giorno , quindi si consiglia "con")
Jürgen A. Erhard,

71

Il fileinputmodulo della libreria standard Python riscriverà un file sul posto se usi il parametro inplace = 1:

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

1
Come dovrebbe funzionare in python3? Ho appena eseguito il porting di un'app che aveva un codice come questo da Python a Python3 e non riuscivo proprio a farlo funzionare bene. La variabile 'line' è un tipo di byte, ho provato a decodificarlo in unicode e quindi a modificarlo e quindi a codificarlo nuovamente in byte, ma non funzionava correttamente. Ha sollevato qualche eccezione che non ricordo dalla parte superiore della mia testa. Le persone che usano fileinput inplace = 1 in python3 hanno successo?
Robru,

1
@Robru: ecco il codice Python 3
jfs

13
Ma non è un problema perché l'hai testato prima su un file non importante giusto?
Paula Livingstone,

33

La riscrittura di un file in atto viene spesso eseguita salvando la vecchia copia con un nome modificato. La gente di Unix aggiunge a ~per contrassegnare quella vecchia. La gente di Windows fa ogni genere di cose - aggiungi .bak o .old - o rinomina completamente il file o mette il ~ in primo piano.

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

Invece di shutil, è possibile utilizzare quanto segue.

import os
os.rename( aFile, aFile+"~" )

1
Sembra buono. Ti chiedi se .readlines () è meglio di iterare la fonte?
Bozdoz,

2
@bozdoz: l'iterazione è migliore poiché readlines legge l'intero file. Non va bene per file di grandi dimensioni. Naturalmente, questo presuppone che tu possa apportare le tue modifiche in modo così localizzato. A volte non puoi, o il tuo codice diventa molto più complicato.
Jürgen A. Erhard,

@ S.Lott: os.rename(aFile, aFile + "~")modificherà il nome del file sorgente, non creando una copia.
Patapoom

14

Il modulo mmap di Python ti permetterà di inserire in un file. Il seguente esempio mostra come può essere fatto in Unix (Windows mmap potrebbe essere diverso). Si noti che questo non gestisce tutte le condizioni di errore e si potrebbe corrompere o perdere il file originale. Inoltre, questo non gestirà le stringhe 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()

È anche possibile farlo senza mmap con i file aperti in modalità 'r +', ma è meno conveniente e meno efficiente in quanto dovresti leggere e archiviare temporaneamente il contenuto del file dalla posizione di inserimento in EOF - che potrebbe essere enorme.


14

Come accennato da Adam, devi prendere in considerazione le limitazioni del tuo sistema prima di poter decidere se hai abbastanza memoria per leggere tutto in memoria, sostituirne parti e riscriverlo.

Se hai a che fare con un file di piccole dimensioni o non hai problemi di memoria, questo potrebbe aiutare:

Opzione 1) Leggi l'intero file in memoria, fai una sostituzione regex sull'intera o parte della linea e sostituiscilo con quella linea più la linea aggiuntiva. Dovrai assicurarti che la 'linea di mezzo' sia univoca nel file o se hai timestamp su ogni linea, questo dovrebbe essere abbastanza affidabile.

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

Opzione 2) Capire la linea di mezzo e sostituirla con quella linea più la linea aggiuntiva.

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

2

Ha scritto una piccola classe per farlo in modo pulito.

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

Quindi puoi usarlo in questo modo:

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

Questo non funziona per me personalmente, aggiunge testo al file ma rimuove prima tutto!
Bret Hawker,

In effetti, questo non funziona affatto. Peccato, perché sembrava una buona idea.
Mario Krušelj,

0

Se conosci qualche unix potresti provare quanto segue:

Note: $ indica il prompt dei comandi

Supponi di avere un file my_data.txt con il contenuto in quanto tale:

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

Quindi utilizzando il osmodulo è possibile utilizzare i soliti sedcomandi

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)

Se non sei a conoscenza di sed, dai un'occhiata, è estremamente utile.


3
Non è affatto Pythonic
DarkSuniuM
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.