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?
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?
Respuestas:
Lamentablemente, no hay forma de insertar en el medio de un archivo sin volver a escribirlo. Como lo han indicado los pósters anteriores, puede agregar un archivo o sobrescribir parte de él mediante la búsqueda, pero si desea agregar cosas al principio o en el medio, tendrá que volver a escribirlo.
Esto es una cosa 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 renombro igual que el archivo original.
Esta es una forma buena y segura de hacerlo porque, si la escritura del archivo se bloquea o cancela por cualquier motivo, aún tiene su archivo original intacto.
Depende de lo que quieras hacer. Para agregar puede abrirlo con "a":
with open("foo.txt", "a") as f:
f.write("new line\n")
Si desea pretender algo, primero debe leer el 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
with
declaración en Python 2.5, necesita agregar "from future import with_statement". Aparte de eso, abrir archivos con la with
declaración es definitivamente más legible y menos propenso a errores que el cierre manual.
fileinput
lib de ayuda con maneja la rutina de abrir / leer / modificar / escribir / reemplazar sucia muy bien al usar el inline=True
arg. Ejemplo aquí: stackoverflow.com/a/2363893/47390
f.Close()
El fileinput
módulo de la biblioteca estándar de Python reescribirá un archivo in situ si utiliza el parámetro 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
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 una ~
para marcar la anterior. La gente de Windows hace todo tipo de cosas: agrega .bak o .old, o cambia el nombre del archivo por completo o coloca el ~ al frente 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
, puede usar lo siguiente.
import os
os.rename( aFile, aFile+"~" )
os.rename(aFile, aFile + "~")
modificará el nombre del archivo fuente, no creará una copia.
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 corromper o perder el archivo original. Además, esto no manejará cadenas 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 modo 'r +', pero es menos conveniente y menos eficiente ya que tendría que leer y almacenar temporalmente el contenido del archivo desde la posición de inserción a EOF, lo que podría ser enorme
Como lo mencionó Adam, debe tener en cuenta las limitaciones de su sistema antes de poder decidir si tiene suficiente memoria para leerlo todo en la memoria, reemplazar partes y volver a escribirlo.
Si está tratando con un archivo pequeño o no tiene problemas de memoria, esto podría ayudar:
Opción 1) Lea todo el archivo en la memoria, realice una sustitución de expresiones regulares en toda o parte de la línea y reemplácela con esa línea más la línea adicional. Deberá 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) Calcule la línea media y reemplácela con esa línea más la línea extra.
# 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()
Escribió una pequeña clase 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()
Entonces puedes usarlo 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
Si conoce unix, puede probar lo siguiente:
Notas: $ significa el símbolo del sistema
Digamos que tiene un archivo my_data.txt con contenido como tal:
$ cat my_data.txt
This is a data file
with all of my data in it.
Luego, usando el os
módulo, puede usar los sed
comandos habituales
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)
Si no eres consciente de sed, échale un vistazo, es extremadamente útil.