Søk og erstatte en linje i en fil i Python

stemmer
231

Jeg ønsker å sløyfe over innholdet i en tekstfil og gjøre et søk og erstatt på noen linjer og skrive resultatet tilbake til filen. Jeg kunne først laste hele filen i minnet, og deretter skrive den tilbake, men som sannsynligvis ikke er den beste måten å gjøre det.

Hva er den beste måten å gjøre dette på, innenfor følgende kode?

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
Publisert på 02/09/2008 klokken 09:19
kilden bruker
På andre språk...                            


14 svar

stemmer
232

Den korteste veien vil trolig være å bruke fileinput modulen . For eksempel, legger følgende linjenumre til en fil, på stedet:

import fileinput

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

Hva som skjer her er:

  1. Den opprinnelige filen er flyttet til en sikkerhetskopifil
  2. Den standard utgang ledes tilbake til den originale filen i sløyfen
  3. Dermed eventuelle printuttalelser skrive tilbake til den opprinnelige filen

fileinputhar flere bjeller og fløyter. For eksempel kan det brukes til å automatisk operere på alle filene i sys.args[1:], uten å måtte iterere over dem eksplisitt. Fra og med Python 3.2 gir det også en praktisk sammenheng manager for bruk i en withuttalelse.


Selv fileinputer flott for kast skript, ville jeg være forsiktig med å bruke det i det virkelige kode fordi riktignok det er ikke veldig lesbart eller kjent. I det virkelige (produksjon) kode er det verdt å tilbringe bare noen få linjer med kode for å gjøre prosessen eksplisitt og dermed gjøre koden lesbar.

Det er to alternativer:

  1. Filen er ikke altfor stor, og du kan bare lese den helt til minnet. Deretter lukker filen, åpne den i skrivemodus, og skrive de modifiserte innholdet tilbake.
  2. Filen er for stor til å lagres i minnet; du kan flytte den over til en midlertidig fil og åpne den, lese den linje for linje, skrive tilbake til den opprinnelige filen. Merk at dette krever dobbelt så mye lagringsplass.
Svarte 14/11/2008 kl. 15:47
kilden bruker

stemmer
155

Jeg tror noe sånt som dette bør gjøre det. Det skriver i utgangspunktet innholdet til en ny fil og erstatter den gamle filen med den nye filen:

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)
Svarte 02/09/2008 kl. 09:42
kilden bruker

stemmer
65

Her er et annet eksempel som ble testet, og vil matche Søk og erstatt mønstre:

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)

Eksempel bruk:

replaceAll("/fooBar.txt","Hello\sWorld!$","Goodbye\sWorld.")
Svarte 24/11/2008 kl. 19:02
kilden bruker

stemmer
52

Dette bør fungere: (inplace redigering)

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"),
Svarte 07/09/2009 kl. 10:07
kilden bruker

stemmer
20

Basert på svaret av Thomas Watnedal. Men dette betyr ikke svare på line-til-linjen del av det opprinnelige spørsmålet nøyaktig. Funksjonen kan fortsatt skifte på en linje til linje for linje

Denne implementeringen erstatter filinnholdet uten å bruke midlertidige filer, som en konsekvens filrettigheter forblir uendret.

Også re.sub i stedet for å erstatte, kan regex erstatning i stedet for ren tekst erstatning bare.

Lese filen som en enkelt streng i stedet for linje for linje åpner for flere linjer kamp og erstatning.

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()
Svarte 30/11/2012 kl. 08:51
kilden bruker

stemmer
11

Som lassevk antyder, skrive ut den nye filen som du går, her er noen eksempel kode:

fin = open("a.txt")
fout = open("b.txt", "wt")
for line in fin:
    fout.write( line.replace('foo', 'bar') )
fin.close()
fout.close()
Svarte 02/09/2008 kl. 09:42
kilden bruker

stemmer
10

Hvis du ønsker en generisk funksjon som erstatter enhver tekst med en annen tekst, er dette sannsynligvis den beste veien å gå, spesielt hvis du er en fan av regex-tallet:

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 )
Svarte 18/02/2014 kl. 14:43
kilden bruker

stemmer
7

En mer Pytonske måte ville være å bruke kontekst ledere som koden under:

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)

Du finner hele tekstutdrag her .

Svarte 07/09/2013 kl. 18:39
kilden bruker

stemmer
3

Utvider på @ Kiran svar, som jeg er enig er mer konsis og Pytonske, legger dette kodeker for å støtte lesing og skriving av 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)
Svarte 02/05/2014 kl. 11:15
kilden bruker

stemmer
3

Opprett en ny fil, kopierer linjer fra den gamle til den nye, og gjøre den erstatter før du skriver linjene til den nye filen.

Svarte 02/09/2008 kl. 09:24
kilden bruker

stemmer
1

Bruke hamishmcn svar som en mal jeg var i stand til å søke etter en linje i en fil som samsvarer med min regex og erstatte den med tom streng.

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()
Svarte 17/04/2014 kl. 02:13
kilden bruker

stemmer
0

fileinput er ganske enkelt som nevnt i tidligere svar:

import fileinput

def replace_in_file(file_path, search_text, new_text):
    with fileinput.input(file_path, inplace=True) as f:
        for line in f:
            new_line = line.replace(search_text, new_text)
            print(new_line, end='')

Forklaring:

  • fileinputkan akseptere flere filer, men jeg foretrekker å lukke hver enkelt fil så snart den blir behandlet. Så plasseres enkelt file_pathi withuttalelsen.
  • printuttalelse ikke ut noe når inplace=True, fordi STDOUTblir videresendt til den opprinnelige filen.
  • end=''i printsetningen er å eliminere mellomliggende tomme nye linjer.

Kan brukes på følgende måte:

file_path = '/path/to/my/file'
replace_in_file(file_path, 'old-text', 'new-text')
Svarte 03/10/2019 kl. 13:37
kilden bruker

stemmer
0

For Linux-brukere:

import os
os.system('sed -i \'s/foo/bar/\' '+file_path)
Svarte 28/05/2018 kl. 18:19
kilden bruker

stemmer
0

hvis du fjerner innrykk på like nedenfor, vil den søke og erstatte i flere linjer. Se under for eksempel.

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)
Svarte 02/08/2012 kl. 19:12
kilden bruker

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