Leia e substitua um arquivo em Python

108

Atualmente estou usando este:

f = open(filename, 'r+')
text = f.read()
text = re.sub('foobar', 'bar', text)
f.seek(0)
f.write(text)
f.close()

Mas o problema é que o arquivo antigo é maior do que o novo. Então, acabo com um novo arquivo que tem uma parte do arquivo antigo no final.

compie
fonte

Respostas:

178

Se você não quiser fechar e reabrir o arquivo, para evitar condições de corrida, você pode truncate:

f = open(filename, 'r+')
text = f.read()
text = re.sub('foobar', 'bar', text)
f.seek(0)
f.write(text)
f.truncate()
f.close()

A funcionalidade provavelmente também ficará mais limpa e segura usando openum gerenciador de contexto, que fechará o gerenciador de arquivos, mesmo se ocorrer um erro!

with open(filename, 'r+') as f:
    text = f.read()
    text = re.sub('foobar', 'bar', text)
    f.seek(0)
    f.write(text)
    f.truncate()
nosklo
fonte
Só para ficar claro em minha mente - seu segundo clipe deveria ter f.write(text)depois f.truncate()?
volvox
2
@volvox f.write(text)é anterior f.truncate()neste código; ele escreve o textprimeiro, portanto, .write()o cursor do arquivo é posicionado no final de text. Prosseguir para truncar o arquivo removerá quaisquer bytes restantes que o arquivo possa ter após esse ponto. Neste caso, o resultado final seria o mesmo que truncado antes de escrever.
nosklo de
Para arquivos muito grandes, a leitura de todo o conteúdo do arquivo na memória pode se tornar difícil. Portanto, o fileinputmódulo pode se tornar o método preferido. Quando aprovado inplace=1, ele moverá o arquivo para um local temporário primeiro e, em seguida, gravará um novo arquivo no caminho do nome antigo. Esta operação de movimentação é rápida em sistemas de arquivos unix, porque ela apenas move o sistema de arquivos inode, não o conteúdo completo. Então você pode ler e processar cada linha individualmente para evitar o inchaço da memória. :-)
TrinitronX
16

Provavelmente seria mais fácil e organizado fechar o arquivo depois text = re.sub('foobar', 'bar', text), reabri-lo para escrever (apagando assim o conteúdo antigo) e escrever o texto atualizado nele.

Il-Bhima
fonte
16

O fileinputmódulo tem um inlinemodo para gravar alterações no arquivo que você está processando sem usar arquivos temporários etc. O módulo encapsula muito bem a operação comum de loop nas linhas de uma lista de arquivos, por meio de um objeto que controla de forma transparente o nome do arquivo, número da linha, etc, se desejar inspecioná-los dentro do loop.

import fileinput
for line in fileinput.FileInput("file",inplace=1):
    if "foobar" in line:
         line=line.replace("foobar","bar")
    print line
ghostdog74
fonte
0

Honestamente, você pode dar uma olhada nesta classe que criei que faz operações básicas de arquivo. O método write substitui e append mantém os dados antigos.

class IO:
    def read(self, filename):
        toRead = open(filename, "rb")

        out = toRead.read()
        toRead.close()
        
        return out
    
    def write(self, filename, data):
        toWrite = open(filename, "wb")

        out = toWrite.write(data)
        toWrite.close()

    def append(self, filename, data):
        append = self.read(filename)
        self.write(filename, append+data)
        
CodinGuy
fonte
-2

Tente gravá-lo em um novo arquivo.

f = open(filename, 'r+')
f2= open(filename2,'a+')
text = f.read()
text = re.sub('foobar', 'bar', text)
f.seek(0)
f.close()
f2.write(text)
fw.close()
sk7979
fonte