Używam Pythona i chciałbym wstawić ciąg do pliku tekstowego bez usuwania lub kopiowania pliku. Jak mogę to zrobić?
Używam Pythona i chciałbym wstawić ciąg do pliku tekstowego bez usuwania lub kopiowania pliku. Jak mogę to zrobić?
Odpowiedzi:
Niestety nie ma możliwości wstawienia do środka pliku bez ponownego zapisania. Jak wskazywały poprzednie plakaty, możesz dołączyć do pliku lub nadpisać jego część za pomocą wyszukiwania, ale jeśli chcesz dodać rzeczy na początku lub w środku, musisz go przepisać.
To kwestia systemu operacyjnego, a nie Pythona. Tak samo jest we wszystkich językach.
Zwykle czytam z pliku, wprowadzam modyfikacje i zapisuję je do nowego pliku o nazwie myfile.txt.tmp lub czegoś podobnego. Jest to lepsze niż wczytywanie całego pliku do pamięci, ponieważ plik może być na to za duży. Po utworzeniu pliku tymczasowego zmieniam jego nazwę na taką samą, jak oryginalnego pliku.
Jest to dobry, bezpieczny sposób na zrobienie tego, ponieważ jeśli zapis do pliku ulegnie awarii lub zostanie przerwany z jakiegokolwiek powodu, nadal masz nienaruszony oryginalny plik.
Zależy od tego, co chcesz zrobić. Aby dołączyć, możesz otworzyć go za pomocą „a”:
with open("foo.txt", "a") as f:
f.write("new line\n")
Jeśli chcesz coś wstępnie przygotować, musisz najpierw przeczytać z pliku:
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
instrukcji w Pythonie 2.5, musisz dodać "z przyszłego importu z_statement". Poza tym otwieranie plików z with
instrukcją jest zdecydowanie bardziej czytelne i mniej podatne na błędy niż zamykanie ręczne.
fileinput
bibliotekę pomocniczą, która ładnie obsługuje brudną procedurę otwierania / odczytu / modyfikacji / zapisu / zamiany podczas używania inline=True
arg. Przykład tutaj: stackoverflow.com/a/2363893/47390
f.Close()
fileinput
Moduł biblioteki standardowej Pythona będzie przepisać inplace pliku, jeśli używasz inplace = 1 parametr:
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
Przepisanie pliku w miejscu często polega na zapisaniu starej kopii pod zmienioną nazwą. Użytkownicy Uniksa dodają a, ~
aby zaznaczyć stary. Ludzie z Windows robią różne rzeczy - dodają .bak lub .old - lub całkowicie zmieniają nazwę pliku lub umieszczają ~ na początku nazwy.
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()
Zamiast tego shutil
możesz użyć następujących.
import os
os.rename( aFile, aFile+"~" )
os.rename(aFile, aFile + "~")
zmieni nazwę pliku źródłowego, nie tworząc kopii.
Moduł mmap w Pythonie pozwoli ci wstawić do pliku. Poniższy przykład pokazuje, jak można to zrobić w systemie Unix (mmap systemu Windows może być inny). Należy pamiętać, że nie obsługuje to wszystkich błędów i może spowodować uszkodzenie lub utratę oryginalnego pliku. Ponadto nie obsługuje to ciągów znaków 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()
Można to również zrobić bez mmap z plikami otwartymi w trybie 'r +', ale jest to mniej wygodne i mniej wydajne, ponieważ musiałbyś czytać i tymczasowo przechowywać zawartość pliku od pozycji wstawienia do EOF - co może być ogromny.
Jak wspomniał Adam, musisz wziąć pod uwagę ograniczenia swojego systemu, zanim będziesz mógł zdecydować, czy masz wystarczająco dużo pamięci, aby wczytać wszystko do pamięci, wymień jej części i ponownie zapisz.
Jeśli masz do czynienia z małym plikiem lub nie masz problemów z pamięcią, może to pomóc:
Opcja 1) Wczytaj cały plik do pamięci, wykonaj podstawienie wyrażenia regularnego na całej lub części linii i zastąp ją tą linią plus dodatkową linią. Musisz upewnić się, że „środkowa linia” jest unikalna w pliku lub jeśli w każdej linii znajdują się znaczniki czasu, powinno to być całkiem niezawodny.
# 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()
Opcja 2) Znajdź środkową linię i zastąp ją tą linią plus dodatkową linią.
# 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()
Napisałem małą klasę, aby zrobić to porządnie.
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()
Następnie możesz go użyć w ten sposób:
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
Jeśli znasz jakiegoś Uniksa, możesz spróbować następujących rzeczy:
Uwagi: $ oznacza wiersz polecenia
Załóżmy, że masz plik my_data.txt z taką zawartością:
$ cat my_data.txt
This is a data file
with all of my data in it.
Następnie za pomocą os
modułu możesz użyć zwykłych sed
poleceń
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)
Jeśli nie jesteś świadomy seda, sprawdź to, jest to niezwykle przydatne.