Pesquisar e substituir uma linha em um arquivo em Python

votos
223

Quero loop sobre o conteúdo de um arquivo de texto e fazer uma pesquisa e substituir em algumas linhas e escrever o resultado de volta para o arquivo. Eu poderia primeiro carregar o arquivo inteiro na memória e, em seguida, escrevê-lo de volta, mas que provavelmente não é a melhor maneira de fazê-lo.

Qual é a melhor maneira de fazer isso, no seguinte código?

f = open(file)
for line in f:
    if line.contains('foo'):
        newline = line.replace('foo', 'bar')
        # how to write this newline back to the file
Publicado 02/09/2008 em 10:19
fonte usuário
Em outras línguas...                            


13 respostas

votos
223

O caminho mais curto seria provavelmente para utilizar o módulo FileInput . Por exemplo, o seguinte adiciona números de linha em um arquivo, no local:

import fileinput

for line in fileinput.input("test.txt", inplace=True):
    print "%d: %s" % (fileinput.filelineno(), line),

O que acontece aqui é:

  1. O arquivo original é movido para um arquivo de backup
  2. A saída padrão é redirecionada para o arquivo original dentro do loop
  3. Assim, quaisquer printdeclarações escrever de volta para o arquivo original

fileinputtem mais sinos e assobios. Por exemplo, ele pode ser usado para operar automaticamente em todos os arquivos em sys.args[1:], sem ter que iterar-los explicitamente. Começando com Python 3.2 também fornece um gerente de contexto conveniente para o uso em um withcomunicado.


Enquanto fileinputé ótimo para scripts descartáveis, eu seria cauteloso em usá-lo em código real, porque é certo que não é muito legível ou familiar. No código real (produção) que vale a pena gastar apenas mais algumas linhas de código para tornar o processo explícito e, assim, tornar o código legível.

Existem duas opções:

  1. O arquivo não é muito grande, e você pode apenas lê-lo totalmente para a memória. Em seguida, feche o arquivo, abra-o novamente no modo de escrita e escrever o conteúdo modificado de volta.
  2. O arquivo é muito grande para ser armazenado na memória; você pode movê-lo para um arquivo temporário e abrir isso, lê-lo linha por linha, escrever de volta para o arquivo original. Note que isso requer o dobro do armazenamento.
Respondeu 14/11/2008 em 16:47
fonte usuário

votos
147

Eu acho que algo como isso deve fazê-lo. Basicamente, grava o conteúdo a um novo arquivo e substitui o arquivo antigo com o novo arquivo:

from tempfile import mkstemp
from shutil import move
from os import fdopen, remove

def replace(file_path, pattern, subst):
    #Create temp file
    fh, abs_path = mkstemp()
    with fdopen(fh,'w') as new_file:
        with open(file_path) as old_file:
            for line in old_file:
                new_file.write(line.replace(pattern, subst))
    #Remove original file
    remove(file_path)
    #Move new file
    move(abs_path, file_path)
Respondeu 02/09/2008 em 10:42
fonte usuário

votos
65

Aqui está outro exemplo que foi testado, e irá corresponder a pesquisar e substituir padrões:

import fileinput
import sys

def replaceAll(file,searchExp,replaceExp):
    for line in fileinput.input(file, inplace=1):
        if searchExp in line:
            line = line.replace(searchExp,replaceExp)
        sys.stdout.write(line)

Exemplo de utilização:

replaceAll("/fooBar.txt","Hello\sWorld!$","Goodbye\sWorld.")
Respondeu 24/11/2008 em 20:02
fonte usuário

votos
49

Isso deve funcionar: (inplace edição)

import fileinput

# Does a list of files, and
# redirects STDOUT to the file in question
for line in fileinput.input(files, inplace = 1): 
      print line.replace("foo", "bar"),
Respondeu 07/09/2009 em 11:07
fonte usuário

votos
18

Com base na resposta por Thomas Watnedal. No entanto, isto não responde a parte da linha-a-linha da pergunta original exatamente. A função ainda pode substituir numa base de linha-a-linha

Esta implementação substitui o conteúdo do arquivo sem usar arquivos temporários, como um permissões de arquivo conseqüência permanecem inalterados.

Também re.sub em vez de substituir, permite a substituição regex em vez de apenas a substituição de texto simples.

Ler o arquivo como uma única cadeia em vez de linha a linha permite o jogo várias linhas e substituição.

import re

def replace(file, pattern, subst):
    # Read contents from file as a single string
    file_handle = open(file, 'r')
    file_string = file_handle.read()
    file_handle.close()

    # Use RE package to allow for replacement (also allowing for (multiline) REGEX)
    file_string = (re.sub(pattern, subst, file_string))

    # Write contents to file.
    # Using mode 'w' truncates the file.
    file_handle = open(file, 'w')
    file_handle.write(file_string)
    file_handle.close()
Respondeu 30/11/2012 em 09:51
fonte usuário

votos
10

Como lassevk sugere, escrever o novo arquivo que você vá, aqui está um exemplo de código:

fin = open("a.txt")
fout = open("b.txt", "wt")
for line in fin:
    fout.write( line.replace('foo', 'bar') )
fin.close()
fout.close()
Respondeu 02/09/2008 em 10:42
fonte usuário

votos
9

Se você está querendo uma função genérica que substitui qualquer texto com algum outro texto, este é provavelmente o melhor caminho a percorrer, especialmente se você é um fã de regex de:

import re
def replace( filePath, text, subs, flags=0 ):
    with open( filePath, "r+" ) as file:
        fileContents = file.read()
        textPattern = re.compile( re.escape( text ), flags )
        fileContents = textPattern.sub( subs, fileContents )
        file.seek( 0 )
        file.truncate()
        file.write( fileContents )
Respondeu 18/02/2014 em 15:43
fonte usuário

votos
6

Uma maneira mais Python seria a utilização de gerentes de contexto como o código abaixo:

from tempfile import mkstemp
from shutil import move
from os import remove

def replace(source_file_path, pattern, substring):
    fh, target_file_path = mkstemp()
    with open(target_file_path, 'w') as target_file:
        with open(source_file_path, 'r') as source_file:
            for line in source_file:
                target_file.write(line.replace(pattern, substring))
    remove(source_file_path)
    move(target_file_path, source_file_path)

Você pode encontrar o trecho completo aqui .

Respondeu 07/09/2013 em 19:39
fonte usuário

votos
3

Expandindo @ resposta de Kiran, que eu concordo é mais sucinto e Pythonic, isto adiciona codecs para suportar a leitura e escrita de UTF-8:

import codecs 

from tempfile import mkstemp
from shutil import move
from os import remove


def replace(source_file_path, pattern, substring):
    fh, target_file_path = mkstemp()

    with codecs.open(target_file_path, 'w', 'utf-8') as target_file:
        with codecs.open(source_file_path, 'r', 'utf-8') as source_file:
            for line in source_file:
                target_file.write(line.replace(pattern, substring))
    remove(source_file_path)
    move(target_file_path, source_file_path)
Respondeu 02/05/2014 em 12:15
fonte usuário

votos
3

Criar um novo arquivo, copiar linhas do velho para o novo, e fazer a substituição antes de escrever as linhas para o novo arquivo.

Respondeu 02/09/2008 em 10:24
fonte usuário

votos
1

Usando resposta de hamishmcn como um modelo eu era capaz de procurar uma linha em um arquivo que correspondem ao meu regex e substituí-lo com a corda vazia.

import re 

fin = open("in.txt", 'r') # in file
fout = open("out.txt", 'w') # out file
for line in fin:
    p = re.compile('[-][0-9]*[.][0-9]*[,]|[-][0-9]*[,]') # pattern
    newline = p.sub('',line) # replace matching strings with empty string
    print newline
    fout.write(newline)
fin.close()
fout.close()
Respondeu 17/04/2014 em 03:13
fonte usuário

votos
0

Para os usuários do Linux:

import os
os.system('sed -i \'s/foo/bar/\' '+file_path)
Respondeu 28/05/2018 em 18:19
fonte usuário

votos
0

Se você remover o recuo no como abaixo, ele irá procurar e substituir em várias linhas. Veja abaixo, por exemplo.

def replace(file, pattern, subst):
    #Create temp file
    fh, abs_path = mkstemp()
    print fh, abs_path
    new_file = open(abs_path,'w')
    old_file = open(file)
    for line in old_file:
        new_file.write(line.replace(pattern, subst))
    #close temp file
    new_file.close()
    close(fh)
    old_file.close()
    #Remove original file
    remove(file)
    #Move new file
    move(abs_path, file)
Respondeu 02/08/2012 em 20:12
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more