2008-09-24 22 views
138

Estoy usando Python y me gustaría insertar una cadena en un archivo de texto sin eliminar o copiar el archivo. ¿Cómo puedo hacer eso?¿Cómo modificar un archivo de texto?

+1

http://stackoverflow.com/questions/1325905/inserting-line-at-specified-position-of-a-text-file -in-python/1325927 # 1325927 – pmav99

+1

Puede consultar la respuesta [this] (http://stackoverflow.com/a/1325927/2276527) de Alex Martelli. – Gogo

+0

http://stackoverflow.com/a/4358169/538284 –

Respuesta

86

Depende de lo que quieras hacer. Para anexar se puede abrir con "a":

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

Si desea antepóngale algo que tienes que leer desde el primer archivo:

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 pequeña adición, para usar la instrucción 'with' en Python 2.5, debe agregar" from __future__ import with_statement ". Aparte de eso, abrir archivos con la instrucción 'with' es definitivamente más legible y menos propenso a errores que el cierre manual. –

+2

Puede considerar el archivo 'fileinput' helper lib maneja la rutina dirty open/read/modify/write/replace muy bien cuando se usa el argumento 'inline = True'. Ejemplo aquí: http://stackoverflow.com/a/2363893/47390 – mikegreenberg

+2

Simplemente no se olvide de cerrar el archivo. 'f.Close()' –

112

Desafortunadamente, no hay manera de insertar en el medio de un archivo sin volver a escribirlo. Como indicaron los carteles anteriores, puede agregar a un archivo o sobrescribir parte del mismo utilizando la búsqueda, pero si desea agregar cosas al principio o al medio, tendrá que volver a escribirlo.

Esto es algo del sistema operativo, no una cosa de Python. Es lo mismo en todos los idiomas.

Lo que suelo hacer es leer el archivo, hacer las modificaciones y escribirlo en un nuevo archivo llamado myfile.txt.tmp o algo así. Esto es mejor que leer todo el archivo en la memoria porque el archivo puede ser demasiado grande para eso. Una vez que se completa el archivo temporal, lo renombre igual que el archivo original.

Esta es una forma buena y segura de hacerlo, ya que si el archivo se cuelga o aborta por alguna razón, usted todavía tiene su archivo original intacto.

+3

¿Las herramientas de Unix como awk/sed hacen algo similar en su código? –

+0

No es cierto que esto sea igual en todos los idiomas. En ActionScript: fileStream.openAsync (filename, FileMode.UPDATE); Luego puedo ir a cualquier parte del archivo que quiero y cambiar cualquier cosa. – AndrewBenjamin

+0

@AndrewBenjamin ¿Sabes qué llamadas al sistema está haciendo ActionScript? ¿Existe la posibilidad de que openAsync lea el archivo y escriba uno nuevo después de la llamada? – AlexLordThorsen

29

La reescritura de un archivo en su lugar a menudo se realiza guardando la copia anterior con un nombre modificado. La gente de Unix agrega un ~ para marcar el anterior. Los usuarios de Windows hacen todo tipo de cosas: agregan .bak o .old, o renombran el archivo por completo o colocan el ~ en la parte frontal del nombre.

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

En lugar de shutil, se puede utilizar el siguiente.

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

Se ve bien. ¿Se pregunta si .readlines() es mejor que repetir la fuente? – bozdoz

+2

@bozdoz: iterar es mejor ya que readlines lee todo el archivo. No es bueno para archivos grandes. Por supuesto, esto supone que puede hacer sus modificaciones de una manera tan localizada. A veces no puedes, o tu código se vuelve mucho más complicado. –

13

El módulo mmap de Python le permitirá insertar en un archivo. El siguiente ejemplo muestra cómo se puede hacer en Unix (Windows mmap puede ser diferente). Tenga en cuenta que esto no maneja todas las condiciones de error y puede dañar o perder el archivo original. Además, esto no manejará cadenas de 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() 

También es posible hacer esto sin mmap con archivos abiertos en 'r +' modo, pero es menos conveniente y menos eficiente que lo tienes que leer y almacenar temporalmente los contenidos del archivo de la posición de inserción a EOF, que podría ser enorme.

52

El módulo fileinput de la biblioteca estándar de Python volverá a escribir un inplace archivo si se utiliza el inplace = 1 parámetro:

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 
+5

Esta es IMO la respuesta correcta. –

+1

¿Cómo se espera que funcione en python3? Acabo de portar una aplicación que tenía un código como este de python a python3 y simplemente no pude hacer que esto funcionara en absoluto. La variable 'line' es un tipo de bytes, intenté decodificarlo en unicode y luego modificarlo y luego codificarlo de nuevo en bytes, pero simplemente no funcionaría correctamente. Levantó una excepción que no puedo recordar en la parte superior de mi cabeza. ¿Las personas que usan fileinput inplace = 1 en python3 tienen algún éxito? – robru

+0

@Robru: aquí está [código de Python 3] (http://stackoverflow.com/a/20593644/4279) – jfs

9

Como se ha mencionado por Adam usted tiene que tomar sus limitaciones del sistema en consideración antes de que pueda decidir Al acercarse si tiene suficiente memoria para leer todo en la memoria, reemplace partes y vuelva a escribir.

Si usted está tratando con un pequeño archivo o que no tienen problemas de memoria que esto podría ayudar:

Opción 1) Leer archivo en la memoria, hacer un cambio de expresiones regulares en la totalidad o parte de la línea de y reemplázalo con esa línea más la línea adicional. Tendrá que asegurarse de que la 'línea media' sea única en el archivo o si tiene marcas de tiempo en cada línea, esto debería ser bastante confiable.

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

Opción 2) Figura cabo línea media, y reemplazarlo con esa línea, más la línea adicional.

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

Escribí una clase pequeña para hacer esto limpiamente.

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

continuación, puede utilizar de esta manera:

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 
Cuestiones relacionadas